多关键词正则匹配需先按长度降序排序并转义元字符,中文场景用Unicode边界断言替代,replace须用捕获组$1保留大小写,且仅操作TEXT_NODE节点,关键词还需双重转义(HTML实体+正则元字符)。

多关键词正则匹配必须按长度降序排序
直接用 words.join('|') 拼正则,短词会提前截断长词。比如搜 ["app", "apple pie"],app 先被匹配,apple pie 就永远匹配不上。
正确做法是先对关键词数组去重、过滤空值,再按字符串长度从长到短排序:
wordsToHighlight.filter(w => w).sort((a, b) => b.length - a.length)- 转义每个词中的正则元字符:
w.replace(/[.*+?^${}()|[]]/g, '$&') - 最终正则写成:
new RegExp(`(${escapedWords.join('|')})`, 'gi')
中文场景需替换单词边界 为 Unicode 边界断言
英文用 可靠,但中文没有空格分隔, 在汉字间不生效,导致“服务器”被高亮成“服务”+“器”,语义断裂。
改用前后 Unicode 断言更稳妥:
- 匹配前:确保前面是空白、汉字或行首 ——
(? - 匹配后:确保后面是空白、汉字或行尾 ——
(?=[\s\u4e00-\u9fa5]|$) - 完整正则:
new RegExp(`(?
replace 回调里必须用捕获组 $1 保留原文大小写
如果写成 text.replace(regex, '<mark>' + keyword + '</mark>'),搜 ERROR 会把页面里的 error 强行改成大写,破坏原始语义。
必须用括号捕获,并在回调或替换字符串中引用实际匹配内容:
- 正则带捕获:
new RegExp(`(${escaped})`, 'gi') - 替换用
'<mark>$1</mark>'(字符串方式)或函数式回调:(match) => `<mark>${match}</mark>` - 这样
error还是error,Error还是Error
高亮必须只操作 TEXT_NODE,不能碰 innerHTML
对整个容器的 innerHTML 做正则替换,会把 <img src="logo.png"> 里的 logo 也包进 <mark></mark>,结果变成 <img src="<mark>logo</mark>.png">,图片直接挂掉。
安全路径是只遍历文本节点:
- 用
document.createTreeWalker(element, NodeFilter.SHOW_TEXT) - 跳过空节点和全空白节点:
!node.nodeValue.trim() - 对每个
node.nodeValue执行匹配和替换,再用node.replaceWith(newNode) - 新节点用
document.createElement('mark')+textContent = match组装,不拼 HTML 字符串
最易被忽略的是:关键词转义要分两层——HTML 实体转义防 XSS,正则元字符转义防语法错误;这两步缺一不可,且顺序不能颠倒。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











