链接里的中文变成百分号了:URL 编码那点事

举报
茉莉风铃 发表于 2026/09/30 14:06:40 2026/09/30
【摘要】 做个链接编码解码小工具时才发现,encodeURI 和 encodeURIComponent 这俩兄弟的区别,很多人一直没搞明白。这篇讲清楚:什么时候该用哪个、哪些字符它俩都不管、为什么 decodeURIComponent 会突然报错、以及用 URLSearchParams 拼参数比手写字符串靠谱在哪。

做链接编码解码小工具的时候,我自己先把这事儿搞混了。明明写了 encodeURI,结果链接里的 & 还是原样待着,拼出来的参数被截断。

查了一圈才把这两个函数的区别理清楚。这篇讲讲,顺便说说几个实操上的坑。

一、为什么需要编码

URL 里有些字符是有特殊含义的:? 分隔路径和参数,& 分隔参数,# 是锚点,= 连接键值。

那你参数值里正好有个 & 怎么办?比如搜索关键词是 C++ & Rust——直接拼进去,服务器会以为 & 后面是下一个参数,关键词被截断了。

所以需要把这些有特殊含义的字符转成百分号加十六进制的形式,这就是 URL 编码(也叫百分号编码)。

encodeURIComponent('C++ & Rust')
// "C%2B%2B%20%26%20Rust"

+ 变成 %2B,空格变成 %20,& 变成 %26,这样就不会被误读了。

二、两个函数的区别

这是最容易搞混的地方:

不编码的字符 用途
encodeURI 保留 ; , / ? : @ & = + $ # 这些 URL 语法字符 编码整个 URL
encodeURIComponent 把这些也全部编码掉 编码参数值

举例子最直观:

const url = 'https://example.com/search?q=hello&page=1'

encodeURI(url)
// "https://example.com/search?q=hello&page=1"   几乎没变,因为都是合法字符

encodeURIComponent(url)
// "https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dhello%26page%3D1"

看到了吧——encodeURIComponent 把 :// 都编码了,所以它绝对不能用来处理整个 URL,那样得到的不是链接了。

正确的用法是整体用 encodeURI、参数值用 encodeURIComponent:

const keyword = 'C++ & Rust'
const url = `https://example.com/search?q=${encodeURIComponent(keyword)}&page=1`
// "https://example.com/search?q=C%2B%2B%20%26%20Rust&page=1"

一句话记:拼进 URL 参数里的每一段值,都用 encodeURIComponent。

三、它们俩都不管的字符

有意思的是,有几个字符两个函数都不编码:

encodeURIComponent("A-Z a-z 0-9 - _ . ! ~ * ' ( )")
// "A-Z%20a-z%200-9%20-_%20.!~*'()"      只有空格被编码了

! ~ * ' ( ) 这几个原样保留。绝大多数场景无所谓,但如果你要严格编码(比如做签名),得手动补一刀:

function strictEncode(str) {
  return encodeURIComponent(str).replace(/[!'()*]/g, c =>
    '%' + c.charCodeAt(0).toString(16).toUpperCase()
  )
}

四、解码会突然报错

写解码工具的时候测试,输入一串乱码,页面直接崩了:

decodeURIComponent('%')
// URIError: URI malformed

decodeURIComponent 遇到不合法的百分号序列会抛异常,不是返回原字符串。所以解码用户输入必须包 try/catch:

function safeDecode(str) {
  try {
    return decodeURIComponent(str)
  } catch (e) {
    return str      // 解不了就原样返回,别让页面崩了
  }
}

这个在做工具页面时特别重要——用户粘贴什么都可能有,一个百分号就能让整个页面白屏。

五、用 URLSearchParams 拼参数,别手写字符串

以前我拼参数是这样的:

const url = `/search?q=${encodeURIComponent(kw)}&page=${page}&size=${size}`

又长又容易漏编码。现在直接用 URLSearchParams:

const params = new URLSearchParams({
  q: 'C++ & Rust',
  page: 1,
  size: 20,
})
const url = `/search?${params}`
// "/search?q=C%2B%2B+%26+Rust&page=1&size=20"

它自动帮你编码,代码干净多了。

但这里有个坑:你会发现上面空格变成了 + 而不是 %20。因为 URLSearchParams 遵循表单提交的编码规则,toString() 时把空格输出成 +。

这在绝大多数情况没问题(服务端都认),但如果你要跟严格按 RFC 3986 解析的后端对接,得自己换回来:

const url = `/search?${params.toString().replace(/\+/g, '%20')}`

反过来解析也一样,URLSearchParams 能正确读取 + 和 %20 两种写法,不用担心。

六、几个容易踩的点

  • 别整个 URL 一起编码。这是最常见的错误,结果就是一堆 %3A%2F%2F,链接废了。
  • 别重复编码。已经编码过一次的字符串再编一次,%20 会变成 %2520(% 自己被编码成 %25),解一次码解不回来。做工具的时候最好加个提示,或者提供"只解码一次"的选项。
  • + 在不同上下文含义不同。在 query string 里它表示空格,但在 path 里它就是个普通的加号。所以 a+b 放在路径里和放在参数里,解码结果可能不一样。
  • 中文编码是 UTF-8。encodeURIComponent('中') 得到 %E4%B8%AD,这是"中"的 UTF-8 三个字节。前后端编码方式要一致,不然解出来是乱码。
  • 编码不等于加密。%E4%B8%AD 看着像密文,其实谁都能解,别拿它当安全手段。
  • 长度限制。URL 有长度上限(各浏览器不同,一般几千字符),超长的参数别塞 URL 里,改用 POST。

文章总结

URL 编码这事儿规则不复杂,就一条主线:整体链接用 encodeURI,拼进参数的值用 encodeURIComponent。真正的坑在细节上——decodeURIComponent 遇到非法输入会抛异常必须 catch、URLSearchParams 会把空格输出成 +、以及别重复编码。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。