一個典型的壞連結

你拼了一個搜尋連結,英文下一切正常,使用者一輸入中文就掛:

https://example.com/search?q=書籤管理 & page=2

伺服器收到的 q書籤管理 ,還多了一個叫 page 的參數——本來屬於查詢值的 & 被當成了分隔符。#=?+ 和空白也有同樣的問題。

解決辦法是百分比編碼(URL 編碼):把不安全的字元替換成 % 加兩個十六進位數字,表示它的 UTF-8 位元組。

書籤管理  →  %E6%9B%B8%E7%B1%A4%E7%AE%A1%E7%90%86
&        →  %26
空白      →  %20

哪些字元是安全的

RFC 3986 把字元分成「未保留」與「保留」兩類,只有未保留字元可以原樣出現:

類別 字元 說明
未保留 A–Z a–z 0–9 - _ . ~ 任何位置都安全
保留(結構類) : / ? # [ ] @ 在 URL 中有結構意義
保留(子分隔符) ! $ & ' ( ) * + , ; = 在查詢字串中有意義
其他 空白、"<>%{}、中文⋯ 必須編碼

保留字元不是「不能用」,而是「有意義」。關鍵問題永遠是:這個 & 是想表達結構,還是想表達資料?如果是資料,就編碼它。

encodeURI 和 encodeURIComponent 的差別

JavaScript 內建兩個編碼函式,行為完全不同:

const raw = 'https://example.com/search?q=書籤 & page=2'

encodeURI(raw)
// https://example.com/search?q=%E6%9B%B8%E7%B1%A4%20&%20page=2
//        保留 : / ? & = —— 用來編碼「一整個 URL」

encodeURIComponent(raw)
// https%3A%2F%2Fexample.com%2Fsearch%3Fq%3D%E6%9B%B8%E7%B1%A4%20%26%20page%3D2
//        連 : / ? & = 一起編碼 —— 用來編碼「一個參數值」

簡單判斷:

  • 編碼完整 URL(自己拼好的)→ 用 encodeURI
  • 編碼要插進 URL 的參數值 → 用 encodeURIComponent

大部分 bug 都來自用錯這一個函式。如果參數值裡還嵌著 URL,基本上一定要用 encodeURIComponent

空白到底是 %20 還是 + ?

兩種都存在,差別是歷史原因:

  • 路徑和標準規定用 %20
  • application/x-www-form-urlencoded(HTML 表單、很多查詢解析器)把空白編碼成 +

在路徑裡,+ 就是字面上的加號,不是空白。這就是「我的 + 怎麼變成空白了」這類問題的根源:解析器把它當成表單編碼資料了。

為什麼會解碼失敗

decodeURIComponent('%E6%9B%B8') 正常,decodeURIComponent('%E6%9B') 會丟出錯誤,decodeURIComponent('100%') 也會——單獨的 % 不是完整的轉義序列。

遇到 URIError 時按順序排查:

  1. 序列被截斷% 後面沒有兩個十六進位數字
  2. 編碼了兩遍%25E6... 解一次得到 %E6...,需要再解一次
  3. 層次搞錯:解碼了本來就沒編碼的內容,例如優惠碼裡的 %

怎麼在瀏覽器裡編解碼

URL 線上編解碼工具 完全在瀏覽器本機執行,不上傳、可離線。

  1. 選擇「編碼」或「解碼」
  2. 貼上文字、URL 或已編碼字串
  3. 點「開始轉換」(或按 Ctrl + Enter
  4. 把結果複製到程式碼或網址列

工具使用 encodeURIComponent / decodeURIComponent,正是處理查詢參數值時需要的行為。

常見問題

應該編碼整個 URL 還是只編碼參數? 只編碼「資料」部分。結構自己拼,每個值用 encodeURIComponent 編碼。

為什麼 %20 有時顯示成 + 表單編碼用 + 表示空白。對接不同解析器時注意轉換。

中文需要編碼嗎? 需要。中文不屬於 ASCII,必須按 UTF-8 位元組做百分比編碼。

能手工解碼嗎? 短字串可以查表,稍長一點還是用工具更可靠。

相關閱讀