會破壞 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 匯入。如果某個值看起來沒問題卻死活匹配不上,先檢查有沒有混進不換行空格。