現在處理編碼:TextEncoder、Uint8Array 的 Base64 方法與 URLSearchParams
整理 JavaScript 現代編碼方式,使用 TextEncoder、TextDecoder 與 Uint8Array 處理 UTF-8 與 Base64,並說明 Base64URL、URLSearchParams 與 encodeURIComponent 的差異。
W
以前處理中文 Base64 時,常看到這種寫法:
btoa(unescape(encodeURIComponent('中文')))
現在不建議再使用這種方式,escape() 與 unescape() 已經 deprecated。
比較清楚的做法是先把文字轉成 UTF-8 位元組,再處理 Base64。
字串 ↔ 位元組
先記住這個流程:
字串 "中文"
↕ TextEncoder / TextDecoder
Uint8Array
↕ toBase64() / fromBase64()
Base64 "5Lit5paH"
文字轉成位元組:
const bytes = new TextEncoder().encode('中文')
// Uint8Array(6) [228, 184, 173, 230, 150, 135]
轉回文字:
new TextDecoder().decode(bytes)
// '中文'
TextEncoder 使用 UTF-8;TextDecoder 則負責將位元組重新轉回文字。
位元組 ↔ Base64
現在 Uint8Array 可以直接處理 Base64:
const bytes = new TextEncoder().encode('中文')
bytes.toBase64()
// '5Lit5paH'
Uint8Array.fromBase64('5Lit5paH')
// Uint8Array(6) [...]
完整寫法:
function encodeBase64(text) {
return new TextEncoder().encode(text).toBase64()
}
function decodeBase64(base64) {
return new TextDecoder().decode(
Uint8Array.fromBase64(base64)
)
}
encodeBase64('中文 👍')
// '5Lit5paHIPCfkY0='
decodeBase64('5Lit5paHIPCfkY0=')
// '中文 👍'
中文與 emoji 都不需要再另外處理。
Base64URL
JWT 或放在 URL 裡的 token 常使用 Base64URL。
它與一般 Base64 的主要差異是:
+ → -
/ → _
通常也會省略結尾的 =。
現在可以直接指定:
bytes.toBase64({
alphabet: 'base64url',
omitPadding: true
})
解碼:
Uint8Array.fromBase64(token, {
alphabet: 'base64url'
})
Query string:URLSearchParams
Base64 與 URL encoding 是不同的東西。
如果要建立 query string,例如:
/search?q=中文&page=1
可以使用 URLSearchParams:
const params = new URLSearchParams({
q: 'A&B 公司',
page: '1'
})
params.toString()
// 'q=A%26B+%E5%85%AC%E5%8F%B8&page=1'
也可以直接搭配 URL:
const url = new URL('https://example.com/search')
url.searchParams.set('q', 'A&B 公司')
url.searchParams.append('tag', '前端')
url.toString()
這樣就不用自己手動拼 query string。
URLSearchParams 與 encodeURIComponent
兩者最容易注意到的差異是空白:
encodeURIComponent('a b')
// 'a%20b'
new URLSearchParams({ q: 'a b' }).toString()
// 'q=a+b'
簡單記:
- query string →
URLSearchParams - URL 路徑片段 →
encodeURIComponent
例如:
const path = `/posts/${encodeURIComponent(slug)}`
不要把 URLSearchParams 拿來處理 URL path。
TextEncoder 與 crypto.subtle
crypto.subtle 處理的是二進位資料。
如果要計算文字的 SHA-256,需要先把字串轉成 UTF-8 位元組:
async function sha256(text) {
const bytes = new TextEncoder().encode(text)
const digest = await crypto.subtle.digest(
'SHA-256',
bytes
)
return new Uint8Array(digest).toHex()
}
如果原本拿到的就是 ArrayBuffer 或 Uint8Array,就不需要再經過 TextEncoder。
簡單對照
| 要做的事 | 現在建議 |
|---|---|
| 字串 → UTF-8 位元組 | new TextEncoder().encode(str) |
| UTF-8 位元組 → 字串 | new TextDecoder().decode(bytes) |
| 字串 → Base64 | new TextEncoder().encode(str).toBase64() |
| Base64 → 字串 | new TextDecoder().decode(Uint8Array.fromBase64(base64)) |
| Base64URL | { alphabet: 'base64url' } |
| 位元組 → Hex | bytes.toHex() |
| Query string | URLSearchParams |
| URL 路徑片段 | encodeURIComponent() |
escape() / unescape() | 不再使用 |
小結
現在處理文字 Base64,可以先記住這兩個方向:
// 文字 → Base64
new TextEncoder().encode(text).toBase64()
// Base64 → 文字
new TextDecoder().decode(
Uint8Array.fromBase64(base64)
)
核心概念就是:
文字
↕
UTF-8 位元組
↕
Base64
URLSearchParams 則是用來處理 URL query string,和 Base64 是不同用途。