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 参数出错时:

  1. 先判断你编码的是完整 URL,还是某个组件。
  2. query 参数值用 encodeURIComponent()
  3. 构造 query string 优先用 URLSearchParams
  4. 检查 %252F%2520 这类重复编码。
  5. 一次只解一层。
  6. 特别注意嵌套 redirect URL。
  7. 用包含 &=?#、空格、中文、日文、Emoji 的值测试。

相关工具

URL 编码解码工具 检查单个值,用 URL 参数解析工具 查看完整 query string 如何被拆分。如果参数里包含 JSON,先解 URL 这一层,再用 JSON 格式化工具 校验。