一个汉字在 UTF-8 里占 3 个字节,「中」是 E4 B8 AD,URL 里只能出现 ASCII 安全字符,所以每个字节前面加 % 转成 %E4%B8%AD。这不是加密,只是换了一种写法。
两个函数的分工
encodeURI:保留: / ? # [ ] @ ! $ & ' ( ) * + , ; =这些结构字符。用于编码整条 URL。encodeURIComponent:连& = ? / #都编码。用于编码参数值。
用反了的后果很典型:把参数值用 encodeURI 处理,值里的 & 没被编码,服务端就把它当成分隔符,参数被截成两半;反过来把整条 URL 用 encodeURIComponent 处理,? 和 / 全变成 %3F %2F,链接直接打不开。
// 正确
const url = '/search?q=' + encodeURIComponent(keyword) + '&page=2';
// 错误:keyword 里的 & 会破坏参数结构
const bad = '/search?q=' + encodeURI(keyword) + '&page=2';
加号是空格,但只在一部分场合
在 application/x-www-form-urlencoded 编码里,空格被写成 +;而 decodeURIComponent 不认识 +,它会原样保留。所以从表单或某些后端接口拿到的查询串里如果含 +,解码前要先替换:
const decodeQuery = (s) =>
decodeURIComponent(s.replace(/\+/g, ' '));
双重编码怎么认出来
看到 %25E4%25B8%25AD 就是双重编码了——%25 是 % 本身。常见于:网关转发时又编了一次,或者前端把已经编码过的值再传给 URLSearchParams。URLSearchParams 的 set/append 会自动编码,所以传进去的必须是原始值,不能再手动 encode 一次。
看不懂的中文乱码
如果解出来是 ä¸ 这类字符,说明字节被当成 Latin-1 解释了。本质是编码声明缺失:浏览器按 ISO-8859-1 解 UTF-8 字节。检查页面 meta charset、接口 Content-Type 是否带 charset=utf-8。