一个汉字在 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。

排查顺序:先确认是「该编码没编码」还是「多编码了一次」,再看 + 与空格,最后查 charset 声明。用URL 编解码可以把参数拆成表格,一眼看出结构有没有被截断。