前端處理 HTML 字元編碼

前端處理 HTML 字元編碼

開發筆記

情境

使用 Nuxt3 製作論壇功能,後端 API 會吐給我使用者發布的文章資訊。

當串接 API 的時候我遇到一個問題,例如標題是:

文章標題 & 文章內容

但後端的回傳給我的卻是:

文章標題 & 文章內容

雖然有點想叫後端幫我改,但考慮到後端可能是為了防止 XSS 攻擊,所以有用類似 PHP 裡 htmlspecialchars() 的 Function 來處理字元編碼。

如果是這樣的話,就只能在前端做處理了。

解法一:正則表達式

我最先想到的是透過正則來解決:

const text = "`文章標題 & 文章內容";
let decodedText = text.replace(/&/g, "&");
// 輸出: '文章標題 & 文章內容'

但因爲論壇沒有限制使用者在標題裡輸入符號,因此像是 :>< 都有可能出現,下面列出幾個常見的字元編碼,想看所有編碼可以參考這邊

字元轉譯字元
&quot;
&&amp;
<&lt;
>&gt;
空格&nbsp;

如果是寫正則判斷處理,就很難包含所有可能性,比較適合只處理少數個案。

解法二:先渲染成 HTML

直接渲染成 HTML 後再取值,這樣任何 HTML 字元編碼都能被正確 decode:

function decodeHtmlEntities(text: string) {
  const textarea = document.createElement("textarea");
  textarea.innerHTML = text;
  return textarea.value;
}
 
const text = "文字 &amp 標題";
let decodedText = decodeHtmlEntities(text);
console.log(decodedText); // 輸出:'文字 & 標題'

缺點也很明顯,必須要生成一個 Dom 來處理。

根據要處理的資料量,除了要考慮操作 Dom 所花費的效能外,如果是 SSR 網頁的話只能在 ClientOnly 時才能取得 document,可能會對網頁初次加載速度造成不小影響。

目前的情境來說,論壇要渲染的的文章數量至少 20 篇以上,而且網頁使用 SSR,所以可能不太適合這個方法。

解法三:使用套件

經過一番搜尋後,找到 he 這個套件來解決,he 能夠使用 JS 編碼和解碼 HTML 字元。

安裝:

# 安裝 he
pnpm add he
 
#安裝型別
pnpm add -D @types/he

安裝完成後就可以直接 import 進來使用:

import he from "he";
 
const text = "`文章標題 &amp; 文章內容";
let decodedText = he.decode(text.title);
 
console.log(decodedText);
// 輸出:'文章標題 & 文章內容'

另外 he 可以使用 encode 編碼:

import he from "he";
 
const textData = "文字<標題>測試";
 
const textDataEncode = he.encode(textData);
 
console.log("encode", textDataEncode);
// 輸出:'&#x6587;&#x5B57;&#x3C;&#x6A19;&#x984C;&#x3E;&#x6E2C;&#x8A66'
 
console.log("decode", he.decode(textDataEncode));
 
// 輸出:'文字<標題>測試'

以目前情境來說,使用 he 應該是最佳解,不過未來說不定有機會使用到前兩種解法,先記錄起來。

參考

Entity - MDN Web Docs Glossary: Definitions of Web-related terms | MDN