Vue JS段落术语替换异常:误处理解释文本中的词汇
术语替换时误匹配解释文本的问题修复
问题背景
我要把文章里属于术语表的特定词汇,替换成包裹在glossary_container类里的原词汇,同时在旁边加个可点击的+按钮展示术语解释,但现有代码出现了异常:
问题示例
术语表内容:
- tree:"hello I am a tree and a branch and I am green"
- flower:"hello I am flower"
- branch:"hello I am branch"
文章原文:"tree has many things, such as a flower,a branch"
预期效果:tree、flower、branch旁边都显示+按钮
实际异常:tree的解释文本里的"branch"被误替换,导致HTML结构错乱,页面显示出问题
问题根源
代码会扫描已经添加了解释框的内容,把解释文本里的术语当成原文内容进行替换了,必须只基于原始文章内容执行替换操作。
当前使用的Vue代码
const content = this.isRtl ? this.post.content_ar : this.post.content_en let modifiedContent = content.replaceAll(/<br(\s*\/)?>/gi, '<br /><br />'); // Loop through each glossary term this.glossaries.forEach((glossary, index) => { const title = this.isRtl ? glossary.title_ar : glossary.title_en const desc = this.isRtl ? glossary.desc_ar : glossary.desc_en let filteredDesc = desc.replaceAll(/[\"']/g, ''); // Check if the glossary term exists in the content if (content.includes(title)) { console.log('content'); // Replace the glossary term with a span containing the term and plus sign modifiedContent = modifiedContent.replace( title, `<span class="glossary_container"><span>${title}</span><span class="plus-sign" data-index="${index}" data-title="${filteredDesc}" style="cursor:pointer">+</span></span>` ); } }); return modifiedContent;
修复方案
核心思路是全程基于原始文章内容做匹配替换,绝对不能在已经插入过解释框的HTML内容上反复操作。下面提供两种可行方案:
方案一:一次性批量替换(推荐)
用正则表达式一次性匹配所有术语,避免循环中修改已被替换的内容:
const content = this.isRtl ? this.post.content_ar : this.post.content_en let modifiedContent = content.replaceAll(/<br(\s*\/)?>/gi, '<br /><br />'); // 提取所有术语标题,转义正则特殊字符防止语法冲突 const termTitles = this.glossaries.map(glossary => { const title = this.isRtl ? glossary.title_ar : glossary.title_en; return title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }); // 构建匹配单词边界的正则,避免部分匹配(比如不会把"branches"里的"branch"当成术语) const termRegex = new RegExp(`\\b(${termTitles.join('|')})\\b`, 'g'); // 一次性替换所有匹配到的术语 modifiedContent = modifiedContent.replace(termRegex, (matchedTerm) => { // 找到匹配术语对应的 glossary 项 const glossary = this.glossaries.find(g => { return this.isRtl ? g.title_ar === matchedTerm : g.title_en === matchedTerm; }); if (!glossary) return matchedTerm; const desc = this.isRtl ? glossary.desc_ar : glossary.desc_en; const filteredDesc = desc.replaceAll(/[\"']/g, ''); const index = this.glossaries.indexOf(glossary); return `<span class="glossary_container"><span>${matchedTerm}</span><span class="plus-sign" data-index="${index}" data-title="${filteredDesc}" style="cursor:pointer">+</span></span>`; }); return modifiedContent;
方案二:占位符中转法
如果想保留原来的循环逻辑,可以先用唯一占位符替换原始内容里的术语,最后再把占位符换成带按钮的HTML:
const content = this.isRtl ? this.post.content_ar : this.post.content_en let modifiedContent = content.replaceAll(/<br(\s*\/)?>/gi, '<br /><br />'); const replaceMap = new Map(); this.glossaries.forEach((glossary, index) => { const title = this.isRtl ? glossary.title_ar : glossary.title_en; const desc = this.isRtl ? glossary.desc_ar : glossary.desc_en; const filteredDesc = desc.replaceAll(/[\"']/g, ''); if (content.includes(title)) { // 生成唯一占位符,避免替换时互相干扰 const placeholder = `__GLOSSARY_TERM_${index}__`; // 先把原始内容里的术语换成占位符 const escapedTitle = title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); modifiedContent = modifiedContent.replace(new RegExp(`\\b${escapedTitle}\\b`, 'g'), placeholder); // 记录占位符对应的最终HTML replaceMap.set(placeholder, `<span class="glossary_container"><span>${title}</span><span class="plus-sign" data-index="${index}" data-title="${filteredDesc}" style="cursor:pointer">+</span></span>`); } }); // 最后统一替换占位符为实际HTML replaceMap.forEach((html, placeholder) => { modifiedContent = modifiedContent.replaceAll(placeholder, html); }); return modifiedContent;
关键注意点
- 两种方案都确保只处理原始文章里的术语,不会碰解释文本里的内容
- 用
\b单词边界正则,防止误匹配包含术语的长单词(比如"branching") - 必须转义术语里的正则特殊字符(如
$、(、.等),否则会导致正则失效
内容的提问来源于stack exchange,提问作者Israa Elaiwa
相关产品推荐
相关产品推荐

