Menu

JavaScript URL 与查询字符串:URL 和 URLSearchParams

如何在 JavaScript 中用 URL 和 URLSearchParams API 解析、构建和修改 URL:不用正则,也不会踩边界情况的坑。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

别再用字符串技巧解析 URL 了

在 URL API 出现之前,人们用 split('?')、正则表达式和运气来切分 URL。大部分时候能用,直到某个值里出现了 &、=、空格或者非 ASCII 字符,就不行了。浏览器和 Node 都自带了一个正规的解析器,用它就好。

一次调用,URL 的每个部分都已经拆分好并正确解码。遇到无效输入时,构造函数会抛出 TypeError,这通常正是你想要的:一个乱七八糟的 URL 应该大声报错,而不是悄悄在下游产生乱七八糟的结果。

读取查询参数

每个 URL 都有一个 .searchParams 属性,它是一个 URLSearchParams 对象,知道如何读写查询字符串:

有几点值得注意:

  • 返回的值已经解码。?name=Ada%20Lovelace 得到的是 "Ada Lovelace"。
  • 所有值都是字符串。"2" 不是 2。需要数字时用 Number() 转换。
  • 键可以重复出现。get 返回第一个匹配的值;getAll 返回所有匹配的值。
  • 不存在的键返回 null 而不是 undefined,所以和 ?? "default" 搭配很好用。

构建查询字符串

你可以用 URLSearchParams 从零开始构建查询字符串,不用手动转义,也不用自己用 & 连接:

也可以从一个对象构建:任何由 [key, value] 对组成的可迭代对象都可以,普通对象也行:

set 与 append 的区别:set 会替换该键已有的值,append 会再添加一个。当一个键本来就可以重复时(标签、筛选条件)用 append;单值参数用 set。

修改 URL

因为 URL 是一个实时联动的对象,修改 searchParams 会自动更新 .search 和 .href:

这是给已有 URL 追加查询参数的惯用写法。不用检查“URL 里是否已经有 ?”,也不用操心该加 & 还是 ?。

你也可以用同样的方式修改 URL 的其他部分:

遍历参数

URLSearchParams 是可迭代的。for...of 会给你 [key, value] 对,另外还有常见的 keys()、values() 和 entries() 辅助方法:

注意,重复的键会出现多次:你会看到 tag = web,然后是 tag = beginner,作为两个独立的条目。这忠实地反映了实际的查询字符串。

如果你想得到一个普通对象,方便调试时打印,可以用 Object.fromEntries,但它会合并重复的键,只保留最后一个值:

调试时没问题。如果有键可能重复,就不对了。

相对 URL 需要一个基础 URL

单独调用 new URL("/search?q=js") 会抛出错误,因为相对路径本身不是一个有效的 URL。把基础 URL 作为第二个参数传进去:

解析规则和浏览器处理 <a href> 的规则一样:以 / 开头表示从主机根目录开始的绝对路径,不带斜杠表示相对于当前路径,.. 表示向上一级。当你根据配置好的基础地址拼装 API URL 时,这非常方便。

在浏览器中,window.location.href 就是现成的基础 URL,可以用来解析当前页面的 URL:

const u = new URL(window.location.href);
const page = u.searchParams.get("page") ?? "1";

处理无效的 URL

URL 构造函数遇到格式错误的输入会抛出错误。这很有用,但也意味着解析用户输入或外部系统发来的任何内容时,都需要 try/catch:

现代环境还提供了 URL.canParse(input),它返回一个布尔值。如果你只是想做校验,就不必写那一套 try/catch:

一个可运行的小例子

把这些组合起来:从 URL 中读取当前的筛选条件,修改它们,然后生成一个可以跳转的新 URL:

传入 null 会删除该参数,传入其他任何值则会设置或覆盖它。每当你构建筛选界面、分页或深层链接时,最终都会以某种形式写出这种模式。

要点总结

  • new URL(string) 把 URL 解析成带标签的各个部分,遇到无效输入会抛出错误。
  • url.searchParams 是一个 URLSearchParams,使用 get、getAll、set、append、delete、has。
  • 编码会自动完成。除非你在手动拼接字符串,否则不需要 encodeURIComponent。
  • 把基础 URL 作为第二个参数传入,就能解析相对路径。
  • 对于不可信的输入,URL.canParse(或 try/catch)就是你的校验工具。

每当你想用 .split('?') 切分 URL,或者用正则表达式提取查询参数时,请改用这些 API。它们更简洁、更正确,而且运行环境已经自带。

常见问题

如何在 JavaScript 中解析 URL?

把字符串传给 URL 构造函数:const u = new URL('https://example.com/path?x=1')。得到的对象提供 protocol、host、pathname、search、hash,以及一个 searchParams 辅助对象。遇到无效的 URL 时它会抛出错误,所以解析不可信的输入时要用 try/catch 包起来。

如何在 JavaScript 中获取查询字符串参数?

使用 url.searchParams.get('name')。它返回解码后的值,如果参数不存在则返回 null。对于可以重复出现的参数(?tag=a&tag=b),用 searchParams.getAll('tag') 以数组形式获取所有值。

URL 和 URLSearchParams 有什么区别?

URL 解析并表示一个完整的 URL:协议、主机、路径、查询字符串、hash。URLSearchParams 只是查询字符串部分,你可以单独用它来构建或解析 a=1&b=2 这样的字符串。每个 URL 实例都有一个 .searchParams 属性,它是一个与该 URL 绑定的 URLSearchParams。

需要手动对查询参数进行编码吗?

不需要。调用 set、append 或读取字符串时,URLSearchParams 会自动对键和值进行编码,能正确处理空格、&、= 和 Unicode。只有在你手动拼接字符串时才需要 encodeURIComponent,而你通常不应该这样做。

Coddy 编程语言插图

和 Coddy 一起学编程

开始