根本原因是双重转义:服务端已对数据做了html实体编码,前端又用字符串拼接进innerhtml,导致&被当作纯文本渲染而非解析为&。

为什么&没转成&反而显示为&?
根本原因是双重转义:服务端已对数据做了HTML实体编码,前端又用字符串拼接进innerHTML。比如后端返回"price & stock",前端写el.innerHTML = `<div>${data.text}</div>`,浏览器会把&当作文本渲染,而不是解析成&。
常见翻车点:
- 模板引擎(如EJS、Jinja)默认转义变量,若传入的已是转义后的字符串,就会变成
& -
textContent赋值后又取innerHTML再塞回去,等于手动触发一次转义 - JSON API响应里字段值含
这类命名实体,但前端没做解码就直插<code>innerHTML
textContent和innerHTML混用时的边界陷阱
安全做法不是“选一个就行”,而是按用途严格区分:
- 纯文本展示(含用户输入、日志、错误信息)→ 一律用
textContent = str - 必须插HTML片段(如富文本编辑器输出)→ 先用
DOMPurify.sanitize()清洗,再innerHTML = cleanHtml - 想转义字符串再插入HTML → 别手写正则,用
escapeHtml(str)函数(内部靠textContent→innerHTML链式转换)
注意:innerText不能替代textContent——它受CSSdisplay: none影响,且会折叠换行和空格。
服务端转义后前端仍出错?检查这三处
即使PHP用了htmlspecialchars()、Python用了html.escape(),前端仍可能渲染失败:
- JavaScript模板字面量中
${}是字符串插值,不进入HTML解析上下文,所以服务端那层转义完全失效 - Vue/React等框架的
v-html/dangerouslySetInnerHTML需显式启用,且仅对可信内容开放 - Tasmota这类嵌入式固件的HTML压缩脚本(如
compress-html-uncompressed.py)会跳过实体处理,原始被当普通字符压缩,导致Web界面解析异常
哪些字符必须转义?只这五个
不是所有符号都要转,只有参与HTML语法解析的5个字符才强制要求:
和<code>>:标签边界符,不转义会触发非法标签解析-
&:实体起始符,不转义可能截断后续内容(如John & Jane变成John)或引入XSS(如<script></script>) -
":在双引号属性值中必须转为",否则破坏title="He said "Hi""结构 -
':在单引号属性值中必须转为',IE8以下不支持',得用'
其他符号如_、-、!不参与解析,无需转义;数字实体(如💔)比命名实体更兼容,尤其对emoji和中文。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











