uni-app中需用正则/#(.*?)#/g提取话题,替换为带data-topic属性的span,通过事件委托绑定点击跳转;禁用v-html执行指令,过滤空话题和不可见字符,CSS加user-select:none。

uni-app 里怎么解析带 #超话题# 的文本
直接用 v-html 渲染原始字符串是危险的,且无法绑定点击事件;uni-app 没有内置的「话题高亮+可点击」组件,得自己拆解、包装、响应。
核心思路:用正则把 #话题内容# 提取出来,替换成带 data-topic 属性的 <span></span>,再用 @tap 捕获点击。
- 正则必须非贪婪匹配:
/#(.*?)#/g,否则会跨多个 # 匹配成一个长串 - 要过滤空话题,比如
##或# #,否则渲染出空白 span 容易误触 - 替换后的内容必须用
v-html渲染,但需确保只渲染你可控的 HTML 片段(不拼接用户输入的任意 HTML)
如何安全地生成可点击的富文本 HTML
不能直接 str.replace(/#(.*?)#/g, '<span data-topic="$1">$&</span>') —— @tap 在 v-html 里完全无效,Vue 模板语法不会被解析。
正确做法是:用原生 span + class + data- 属性标记,再在父容器上用事件委托捕获点击。
- 给外层容器加
id="topic-container"或 class,比如class="rich-text" - 替换时用:
`<span class="topic-tag" data-topic="${match[1]}">${match[0]}</span>` - 在
mounted钩子中用this.$nextTick(() => { ... })确保 DOM 已渲染,再绑定事件委托
mounted() {
this.$nextTick(() => {
const container = this.$refs.container;
if (container) {
container.addEventListener('click', (e) => {
const topicEl = e.target.closest('.topic-tag');
if (topicEl) {
const topic = topicEl.dataset.topic;
uni.navigateTo({ url: `/pages/topic/detail?name=${encodeURIComponent(topic)}` });
}
});
}
});
}为什么不用 uView 或 uni-app 插件里的 rich-text 组件
<rich-text></rich-text> 组件不支持自定义事件绑定,所有标签都是静态渲染,onclick、bindtap 全部失效;第三方插件如 u-parse 默认也不处理 data- 属性或事件委托。
-
<rich-text></rich-text>只接受有限白名单标签(div,p,span,br等),但不支持事件穿透 - 某些插件会自动转义
#或对空格/中文处理异常,导致正则匹配失败 - 真机调试时,iOS 下
addEventListener必须加{ passive: false }才能阻止默认行为(尤其配合下拉刷新时)
正则和样式容易踩的坑
话题可能嵌套在链接、@人、表情之后,也可能前后带标点(如 今天聊#AI#,太强了!),单纯全局 replace 会污染其他结构。
- 别用
/#[^#]+#/g—— 中文、emoji、全角符号会让它提前截断 - 务必加
trim()处理提取出的 topic 内容,避免前后空格导致跳转失败 - CSS 要加
user-select: none,否则长按会选中文字干扰操作 - Android 上点击反馈弱,建议加
cursor: pointer(H5)或hover-class(小程序)
最麻烦的是话题中间含换行或不可见字符(比如微信粘贴进来的零宽空格),这种得在匹配前先 .replace(/\u200b/g, '') 过滤。

















