会破坏 HTML 的五个字符
HTML 解析器把 < 当作标签开始,把 & 当作实体引用开始。只要你的内容里出现这两者,解析器就不再把它当作纯文本。转义的做法是用一个引用替换该字符,解析器会把引用还原成原字符,而不赋予它任何结构含义。
HTML 中真正需要转义的只有五个字符:
& → & —— 必须最先替换,否则后面的转义会被二次转义
< → <
> → >
" → " —— 出现在双引号属性值里时必须转义
' → ' —— 出现在单引号属性值里时必须转义
严格来说,元素文本中只需要处理 & 和 <。另外三个只在文本可能落进属性值时才要紧,而转义那一刻你通常无法预知它最终会去哪里,所以五个全转义才是稳妥习惯。
命名、十进制与十六进制引用
同一个字符有三种写法。不换行空格可以写成 、  或  。命名引用可读性最好,但 HTML5 的名称表约有 2200 条,老解析器认识的远少于此,因此冷门名称存在移植风险。数字引用适用于任何 Unicode 码点,永远安全。HTML5 中所有命名引用都要求结尾分号;少数遗留名称如 & 出于向后兼容仍能在无分号时被解析,但依赖这一点迟早出事。
转义不等于消毒
转义是阻止跨站脚本的关键:如果含 <script> 的用户输入被转义成 <script>,浏览器只会显示字面文本,不会执行任何脚本。但转义必须发生在正确的上下文里。在 <script> 块内、style 属性内或 URL 中,HTML 转义都不够,这些位置各有自己的编码规则。而且转义只应在输出时执行一次。输入时转一次、输出时又转一次,得到的是 &lt; 和一屏乱码。
什么时候需要扩展模式
任何现代规范都不要求转义非 ASCII 字符。UTF-8 文档可以直接包含 é 和 中,而且通常这才是更好的选择:更短、更易读、也更好搜索。扩展模式是为那些字节流必须穿过会破坏非 ASCII 的通道的场景准备的 —— 某些遗留邮件模板、老旧 CMS 字段、声明编码含糊的 XML 流水线,或者卡在 Latin-1 的构建环节。如果你能端到端控制编码,就留在最小模式。
不换行空格的陷阱
是 U+00A0,与普通空格 U+0020 是不同的字符。它阻止换行,也阻止浏览器合并连续空白,所以所见即所得编辑器会大量产出它。因为外观完全一致,它会悄无声息地破坏字符串比较、某些引擎中用 \s 写的正则,以及 CSV 导入。如果某个值看起来没问题却死活匹配不上,先检查有没有混进不换行空格。