一个典型的坏链接

你拼了一个搜索链接,英文下一切正常,用户一输入中文就崩:

https://example.com/search?q=书签管理 & page=2

服务器收到的 q书签管理 ,还多了一个叫 page 的参数——本该属于查询值的 & 被当成了分隔符。#=?+ 和空格也有同样的问题。

解决办法是百分号编码(URL 编码):把不安全字符替换成 % 加两个十六进制数字,表示它的 UTF-8 字节。

书签管理  →  %E4%B9%A6%E7%AD%BE%E7%AE%A1%E7%90%86
&        →  %26
空格      →  %20

哪些字符是安全的

RFC 3986 把字符分成"未保留"和"保留"两类,只有未保留字符可以原样出现:

类别 字符 说明
未保留 A–Z a–z 0–9 - _ . ~ 任何位置都安全
保留(结构类) : / ? # [ ] @ 在 URL 中有结构含义
保留(子分隔符) ! $ & ' ( ) * + , ; = 在查询串中有含义
其他 空格、"<>%{}、中文… 必须编码

保留字符不是"不能用",而是"有含义"。关键问题永远是:这个 & 是想表达结构,还是想表达数据?如果是数据,就编码它。

encodeURI 和 encodeURIComponent 的区别

JavaScript 自带两个编码函数,行为完全不同:

const raw = 'https://example.com/search?q=书签 & page=2'

encodeURI(raw)
// https://example.com/search?q=%E4%B9%A6%E7%AD%BE%20&%20page=2
//        保留 : / ? & = —— 用来编码「一整个 URL」

encodeURIComponent(raw)
// https%3A%2F%2Fexample.com%2Fsearch%3Fq%3D%E4%B9%A6%E7%AD%BE%20%26%20page%3D2
//        连 : / ? & = 一起编码 —— 用来编码「一个参数值」

简单判断:

  • 编码完整 URL(自己拼好的)→ 用 encodeURI
  • 编码要插进 URL 的参数值 → 用 encodeURIComponent

大部分 bug 都来自用错这一个函数。如果参数值里还嵌着 URL,基本上一定要用 encodeURIComponent

空格到底是 %20 还是 + ?

两种都存在,区别是历史原因:

  • 路径和标准规定用 %20
  • application/x-www-form-urlencoded(HTML 表单、很多查询解析器)把空格编码成 +

在路径里,+ 就是字面意义上的加号,不是空格。这就是"我的 + 怎么变成空格了"这类问题的根源:解析器把它当成表单编码数据了。

为什么会解码失败

decodeURIComponent('%E4%B9%A6') 正常,decodeURIComponent('%E4%B9') 会抛错,decodeURIComponent('100%') 也会——单独的 % 不是完整的转义序列。

遇到 URIError 时按顺序排查:

  1. 序列被截断% 后面没有两个十六进制数字;
  2. 编码了两遍%25E4... 解一次得到 %E4...,需要再解一次;
  3. 层次搞错:解码了本来就没编码的内容,比如优惠码里的 %

怎么在浏览器里编解码

URL 在线编解码工具 完全在浏览器本地运行,不上传、可离线。

  1. 选择「编码」或「解码」;
  2. 粘贴文本、URL 或已编码字符串;
  3. 点击「开始转换」(或按 Ctrl/ + Enter);
  4. 把结果复制到代码或地址栏。

工具使用 encodeURIComponent / decodeURIComponent,正是处理查询参数值时需要的行为。

常见问题

应该编码整个 URL 还是只编码参数? 只编码"数据"部分。结构自己拼,每个值用 encodeURIComponent 编码。

为什么 %20 有时显示成 + 表单编码用 + 表示空格。对接不同解析器时注意转换。

中文需要编码吗? 需要。中文不属于 ASCII,必须按 UTF-8 字节做百分号编码。

能手工解码吗? 短字符串可以查表,稍长一点还是用工具更靠谱。

相关阅读