encodeURI 和 encodeURIComponent 区别:URL 参数到底怎么编码
URL 编码 bug 通常出现在参数里包含 &、=、?、#、空格、中文、Emoji,或者另一个完整 URL 的时候。JavaScript 里最常见的错误,是应该用 encodeURIComponent() 时用了 encodeURI()。
简单规则:
- 编码完整 URL:用
encodeURI() - 编码参数值或路径片段:用
encodeURIComponent()
为什么区别很重要
encodeURI() 会保留完整 URL 中有结构意义的字符,例如:
: / ? # & =
这适合你已经有一个完整 URL,只想编码空格或非 ASCII 字符的情况。
encodeURIComponent() 会编码更多字符,因为它处理的是 URL 的某一个组成部分,比如 query 参数值。
如果参数值里有 &、=、#,却用了 encodeURI(),query string 就可能被拆坏。
Query 参数示例
假设你要传:
const search = 'a=b & c=d'
错误:
const url = '/search?q=' + encodeURI(search)
里面的 & 可能被当成新的 query 参数分隔符。
正确:
const url = '/search?q=' + encodeURIComponent(search)
这样整个值都会留在 q 参数里。
URL 里再嵌套 URL
登录跳转、OAuth 回调、分享链接都容易遇到这个问题。
const next = 'https://example.com/a?x=1&y=2'
const loginUrl = '/login?next=' + encodeURIComponent(next)
如果用 encodeURI(next),里面的 &y=2 可能变成外层 /login 的参数,而不是留在 next 里。
路径片段示例
路径片段也应该用 encodeURIComponent():
const username = 'Ada Lovelace/notes'
const url = '/users/' + encodeURIComponent(username)
这里的 / 是用户名的一部分,不是路径分隔符,所以必须编码。
如果用 encodeURI(),斜杠会保留,路由结构就变了。
优先使用 URL 和 URLSearchParams
构造 query string 时,现代 JavaScript 有更安全的 API:
const url = new URL('https://example.com/search')
url.searchParams.set('q', 'a=b & c=d')
url.searchParams.set('lang', 'zh-CN')
console.log(url.toString())
URLSearchParams 会帮你处理编码和分隔符,也能减少重复编码的风险。
重复编码
重复编码就是把已经编码过的值又编码一次。
hello world
hello%20world
hello%2520world
%25 是 % 的编码形式。如果你看到 %2520,通常说明原本的 %20 又被编码了一遍。
编码或解码前,先判断你正在处理哪一层:
- 用户原始输入
- query 参数值
- 完整 URL
- JSON payload 里的 URL
- 另一个 URL 里的 redirect 参数
空格:%20 还是 +
URL 里空格常见写法是 %20。表单编码的 query string 中,空格也可能表示为 +。
除非你明确知道目标格式,否则不要手动替换空格。JavaScript 里生成 query string 时,通常用 URLSearchParams 更稳。
排错清单
URL 参数出错时:
- 先判断你编码的是完整 URL,还是某个组件。
- query 参数值用
encodeURIComponent()。 - 构造 query string 优先用
URLSearchParams。 - 检查
%252F、%2520这类重复编码。 - 一次只解一层。
- 特别注意嵌套 redirect URL。
- 用包含
&、=、?、#、空格、中文、日文、Emoji 的值测试。
相关工具
用 URL 编码解码工具 检查单个值,用 URL 参数解析工具 查看完整 query string 如何被拆分。如果参数里包含 JSON,先解 URL 这一层,再用 JSON 格式化工具 校验。