一個典型的壞連結
你拼了一個搜尋連結,英文下一切正常,使用者一輸入中文就掛:
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 時按順序排查:
- 序列被截斷:
%後面沒有兩個十六進位數字 - 編碼了兩遍:
%25E6...解一次得到%E6...,需要再解一次 - 層次搞錯:解碼了本來就沒編碼的內容,例如優惠碼裡的
%
怎麼在瀏覽器裡編解碼
URL 線上編解碼工具 完全在瀏覽器本機執行,不上傳、可離線。
- 選擇「編碼」或「解碼」
- 貼上文字、URL 或已編碼字串
- 點「開始轉換」(或按
Ctrl/⌘+Enter) - 把結果複製到程式碼或網址列
工具使用 encodeURIComponent / decodeURIComponent,正是處理查詢參數值時需要的行為。
常見問題
應該編碼整個 URL 還是只編碼參數?
只編碼「資料」部分。結構自己拼,每個值用 encodeURIComponent 編碼。
為什麼 %20 有時顯示成 +?
表單編碼用 + 表示空白。對接不同解析器時注意轉換。
中文需要編碼嗎? 需要。中文不屬於 ASCII,必須按 UTF-8 位元組做百分比編碼。
能手工解碼嗎? 短字串可以查表,稍長一點還是用工具更可靠。