一、五个必须转义的字符
| 字符 | 实体 | 为什么必须转 |
|---|---|---|
| & | & | 它是实体本身的起始符,不转义会导致后续解析错乱 |
| < | &lt; | 会被当成标签开始 |
| > | &gt; | 严格说可省,但成对转义更安全 |
| " | &quot; | 出现在属性值里会截断属性 |
| ' | &#39; | 单引号包裹的属性里会截断 |
其余字符(中文、emoji、常见符号)在 UTF-8 页面里可以直接写,不需要转义。把整段中文逐字转成 中 只会让文件变大、可读性变差。
二、命名实体 vs 数字实体
- 命名实体:
&nbsp;&copy;&mdash;,好读,但 HTML 只预定义了很小一部分(约 2000 个,实际常用的几十个)。 - 数字实体:
&#39;(十进制)或&#x27;(十六进制),任何 Unicode 字符都能表示。
XML 只认五个预定义实体(&lt; &gt; &amp; &quot; &apos;),自定义命名实体在 XML 里会解析失败,跨格式输出时要改用数字实体。
三、转义顺序:& 必须第一个处理
这是最经典的 bug:
// 错误:先转 < 再转 &,会把刚生成的 < 又转一遍
s.replace(/</g, '<').replace(/&/g, '&');
// "<" → "&lt;" ← 页面上显示 <
// 正确:先 & 后其他
s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
.replace(/"/g, '"').replace(/'/g, ''');
页面出现 &lt; 或 &amp;,九成是转义了两次(后端转一次、前端又转一次),或者顺序写反了。修复时不要在前端「反转义一次」了事,应该找到重复转义的那一层去掉。
四、不同上下文的规则不一样
- HTML 文本节点:转
& < >即可,引号可留。 - 双引号属性值:必须转
",'可留。 - 单引号属性值:必须转
'。 <script>内部:HTML 转义无效,需要 JSON 序列化或专用转义(<写成\u003c),否则</script>会提前闭合标签。<textarea>/<title>:只支持文本,标签不会生效,尖括号仍建议转义以免截断。- URL 参数:用 URL 编码,不是 HTML 实体。
一个常见误区是「只要做了 HTML 转义就安全」。实际上属性没加引号、或者把用户输入拼进 onclick、href="javascript:",转义了照样能被利用。
五、什么时候不要转义
- 富文本编辑器产出的 HTML(本身就该是标签)——这时要做的是白名单过滤,不是转义。
- 自己写死的模板片段。
- JSON 响应体里的内容(JSON 有自己的转义规则,不是 HTML 实体)。
判断标准很简单:这段字符串是要「显示成文字」还是「当成结构解析」。前者转义,后者过滤。
六、快速自查
拿不准某段内容转义后是什么效果,用 HTML 实体编解码工具 双向对照;如果是从接口拿到的 JSON 里带一堆实体,先用 JSON 格式化 看清原始字符串,再判断该在哪一层处理。
小结
五个必转字符里 & 要最先处理;转义两次是 &lt; 的唯一来源;<script> 内不能用 HTML 转义;富文本要过滤而不是转义。