隆重推出 WHATWG URL API,该 API 已在 Node.js 8.0 中正式实现!

大家好,
我是开发团队野生队的成员 Mandai。

虽然已经过去一段时间了,但 Node.js 8.0.0 于 2017 年 5 月 30 日发布。
从该版本开始,npm 版本 5.0.0 已被捆绑,并且缓存相关的代码也已重写,使其速度更快。

官方还发布了一条推文,对比了新版本和旧版本的速度。在这个例子中,安装速度似乎只有旧版本的五分之一。

当前 V8 引擎版本为 5.8,但似乎兼容 V8 5.9 和 V8 6.0。未来版本的 V8 引擎可能会升级以进一步提升速度。→ Node.js 8.0 已发布。它包含 npm 5.0 包、Node.js API 以及对 WHATWG URL 解析器的官方支持。- Publickey

这次,我们将研究 WHATWG URL API,该 API 已在 Node.js 8.0 中正式实现。


 

WHATWG URL API是什么?

WHATWG URL API 自 Node.js 7 版本以来就已存在,但在 8.0.0 版本中正式发布。
你们中的许多人可能已经使用过它,但由于它处于“实验性”状态,你们可能对在生产环境中使用它有些犹豫。

此 API 旨在规范 URL 解析,并作为现有 url 模块的扩展提供。

const URL = require('url').URL; const beyondUrl = new URL('http://www.beyondjapan.com/?abc=123&xyz=999#first'); console.log(beyondUrl); // 结果 URL { href: 'http://www.beyondjapan.com/?abc=123&xyz=999#first', origin: 'http://www.beyondjapan.com', protocol: 'http:', username: '', password: '', host: 'www.beyondjapan.com', hostname: 'www.beyondjapan.com', port: '', pathname: '/', search: '?abc=123&xyz=999', searchParams: URLSearchParams { 'abc' => '123', 'xyz' => '999' }, hash: '#first' }

 

当然,您也可以使用 url 模块,感觉和以前一样。

const url = require('url'); const beyondUrl = 'http://www.beyondjapan.com/?abc=123&xyz=999#first'; console.log(url.parse(beyondUrl)); // 结果 URL { protocol: 'http:', slashes: true, auth: null, host: 'www.beyondjapan.com', port: null, hostname: 'www.beyondjapan.com', hash: '#first', search: '?abc=123&xyz=999', query: 'abc=123&xyz=999', pathname: '/', path: '/?abc=123&xyz=999', href: 'http://www.beyondjapan.com/?abc=123&xyz=999#first' }

 

令人困惑的是,虽然对象名称略有不同,但对象输出的内容也略有差异。
在 WHATWG URL API 的响应中,查询字符串会被解析,并附带一个名为 searchParams 的键返回,这很方便。

光凭这一点就让我很想用它了。

 

URL 对象行为

通过 WHATWG API 返回的 URL 对象,您还可以访问每条数据。

const u = 'http://www.beyondjapan.com/?abc=123&xyz=999#first'; const URL = require('url').URL; const beyondUrl = new URL(u); console.log(beyondUrl.hostname); // 结果 www.beyondjapan.com

 

请尝试使用其他主机名。

const u = 'http://www.beyondjapan.com/?abc=123&xyz=999#first'; const URL = require('url').URL; const beyondUrl = new URL(u); beyondUrl.hostname = 'example.com'; console.log(beyondUrl); // 结果 URL { href: 'http://example.com/?abc=123&xyz=999#first', origin: 'http://example.com', protocol: 'http:', username: '', password: '', host: 'example.com', hostname: 'example.com', port: '', pathname: '/', search: '?abc=123&xyz=999', searchParams: URLSearchParams { 'abc' => '123', 'xyz' => '999' }, hash: '#first' }

 

主机名会被识别并重写,所以即使你执行以下操作,也只会更改主机名。

const u = 'http://www.beyondjapan.com/?abc=123&xyz=999#first'; const URL = require('url').URL; const beyondUrl = new URL(u); beyondUrl.hostname = 'example.com:443'; // 尝试添加端口号 console.log(beyondUrl); // 结果 URL { href: 'http://example.com/?abc=123&xyz=999#first', origin: 'http://example.com', protocol: 'http:', // 用户名:'', 密码:'', host: 'example.com', hostname: 'example.com', port: '', // 路径名:'/', search: '?abc=123&xyz=999', searchParams: URLSearchParams { 'abc' => '123', 'xyz' => '999' }, hash: '#first' }

 

如果要更改端口号,需要进行相应的更改。

const u = 'http://www.beyondjapan.com/?abc=123&xyz=999#first'; const URL = require('url').URL; const beyondUrl = new URL(u); beyondUrl.port = 443; console.log(beyondUrl); // 结果 URL { href: 'http://www.beyondjapan.com:443/?abc=123&xyz=999#first', origin: 'http://www.beyondjapan.com:443', protocol: 'http:', // 未更改 username: '', password: '', host: 'www.beyondjapan.com:443', hostname: 'www.beyondjapan.com', port: '443', pathname: '/', search: '?abc=123&xyz=999', searchParams: URLSearchParams { 'abc' => '123', 'xyz' => '999' }, hash: '#first' }

 

但是,如果更换主机,情况似乎就不同了。

const u = 'http://www.beyondjapan.com/?abc=123&xyz=999#first'; const URL = require('url').URL; const beyondUrl = new URL(u); beyondUrl.host = 'example.com:443'; console.log(beyondUrl); // 结果 URL { href: 'http://example.com:443/?abc=123&xyz=999#first', origin: 'http://example.com:443', protocol: 'http:', // 用户名未更改: '', password: '', host: 'example.com:443', // 主机名已更改: 'example.com', // 端口已更改: '443', // 路径名已更改: '/', search: '?abc=123&xyz=999', searchParams: URLSearchParams { 'abc' => '123', 'xyz' => '999' }, hash: '#first' }

 

URLSearchParams 类

我们之前已经了解了 URL 对象的行为,现在我们将了解从 URL.searchParams 获取的 URLSearchParams 类。
该对象是 Node.js 7 中实现的一个类,它解析查询字符串并提供 getter/setter 函数。

官方文档也将其与查询字符串模块进行了比较,但 URLSearchParams 类似乎不如查询字符串模块灵活,所以这并不意味着查询字符串模块就变得不必要了。

URLSearchParams 类是 url 模块中的一个独立类,因此可以独立使用。
正因如此,它不仅可用于解析 URL,还可用于生成 URL。

const {URLSearchParams} = require('url'); const qs = 'abc=123&xyz=456&aaa=789'; const qsObject = { abc:123, xyz:456, aaa:789 }; const qsIterable = [ ['abc', 123], ['xyz', 456], ['aaa', 789], ]; const qsMap = new Map(); qsMap.set('abc', 123); qsMap.set('xyz', 456); qsMap.set('aaa', 789); function* qsGenerator(){ yield ['abc', 123]; yield ['xyz', 456]; yield ['aaa', 789]; const params1 = new URLSearchParams(qs); // 可以是常规查询字符串格式字符串 const params2 = new URLSearchParams(qsObject); // 可以是常规对象 const params3 = new URLSearchParams(qsIterable); // 可以是迭代器 const params4 = new URLSearchParams(qsMap); // 可以是 Map 对象 const params5 = new URLSearchParams(qsGenerator()); // 可以是生成器 console.log(params1); console.log(params2); console.log(params3); console.log(params4); console.log(params5); // 结果 URLSearchParams { 'abc' => '123', 'xyz' => '456', 'aaa' => '789' } URLSearchParams { 'abc' => '123', 'xyz' => '456', 'aaa' => '789' } URLSearchParams { 'abc' => '123', 'xyz' => '456', 'aaa' => '789' } URLSearchParams { 'abc' => '123', 'xyz' => '456', 'aaa' => '789' }

 

他是个非常挑食的人,却什么都爱吃,包括对象、数组、地图对象和生成器。

创建的 URLSearchParams 对象具有多种方法。

 

附加

const {URLSearchParams} = require('url'); const qs = 'abc=123&xyz=456&aaa=789'; const params = new URLSearchParams(qs); params.append('bbb', 963); console.log(params.toString()) // 结果 // abc=123&xyz=456&aaa=789&bbb=963

 

 

删除

const {URLSearchParams} = require('url'); const qs = 'abc=123&xyz=456&aaa=789'; const params = new URLSearchParams(qs); params.delete('bbb'); console.log(params.toString()); // 结果 // abc=123&xyz=456&aaa=789

 

 

返回一个用于遍历条目的迭代器

const {URLSearchParams} = require('url'); const qs = 'abc=123&xyz=456&aaa=789'; const params = new URLSearchParams(qs); for (let v of params.entries()) console.log(v); // 结果 /* [ 'abc', '123' ] [ 'xyz', '456' ] [ 'aaa', '789' ] */

 

 

在完整循环中使用 forEach

const {URLSearchParams} = require('url'); const qs = 'abc=123&xyz=456&aaa=789'; const params = new URLSearchParams(qs); params.forEach((value, key, p) => { console.log(value, key, p); }) // 结果 /* 123 abc URLSearchParams { 'abc' => '123', 'xyz' => '456', 'aaa' => '789' } 456 xyz URLSearchParams { 'abc' => '123', 'xyz' => '456', 'aaa' => '789' } 789 aaa URLSearchParams { 'abc' => '123', 'xyz' => '456', 'aaa' => '789' } */

 

 

Get 方法返回参数键的值

const {URLSearchParams} = require('url'); const qs = 'abc=123&xyz=456&aaa=789'; const params = new URLSearchParams(qs); console.log(params.get('abc')); // 结果 // 123

 

 

getAll 返回参数键的所有值

既然我们要讨论它与 get 的区别,我就创建了两个示例。

const {URLSearchParams} = require('url'); const qs = 'abc=123&xyz=456&aaa=789'; const params = new URLSearchParams(qs); console.log(params.getAll('abc')) // 结果 // [ '123' ]

 

const {URLSearchParams} = require('url'); const qs = 'abc=123&xyz=456&aaa=789&abc=777'; const params = new URLSearchParams(qs); console.log(params2.getAll('abc')) // 结果 // [ '123', '777' ]

 

URLSearchParams 对象允许重复键,因此它有一个 getAll 方法。

顺便一提,对于 get 方法,规范是返回重复键中的第一个已注册键,因此该值可能只能在 getAll 或循环中访问。

 

必须检查是否存在

const {URLSearchParams} = require('url'); const qs = 'abc=123&xyz=456&aaa=789'; const params = new URLSearchParams(qs); console.log(params.has('abc')); // 结果 // true

 

 

keys 返回一个键的迭代器

const {URLSearchParams} = require('url'); const qs = 'abc=123&xyz=456&aaa=789'; const params = new URLSearchParams(qs); for (let k of params.keys()) console.log(k); // 结果 /* abc xyz aaa */

 

 

覆盖集

const {URLSearchParams} = require('url'); const qs = 'abc=123&xyz=456&aaa=789'; const params = new URLSearchParams(qs); params.set('vvv', 247); console.log(params.toString()); // 结果 // abc=123&xyz=456&aaa=789&vvv=247 params.set('vvv', 247); console.log(params.toString()); // 结果 // abc=123&xyz=456&aaa=789&vvv=247

 

如果对应的键不存在,则执行追加操作;如果键存在,则覆盖键的内容。
如果存在多个键,则删除所有键,然后再执行追加操作。

const qs = 'a=1&a=2&a=3'; const params = new URLSearchParams(qs); console.log(params.toString()); // 此时 // a=1&a=2&a=3 params.set('a', 4); console.log(params.toString()); // 结果 // a=4

 

 

破坏性类型

按名称正序对对象内容进行排序。
不支持反向排序。

请注意,这不会返回一个 URLSearchParams 对象,该对象中的对象顺序会被交换,而是交换已执行对象的顺序(尽管顺序通常不是您关心的事情)。

const {URLSearchParams} = require('url'); const qs = 'abc=123&xyz=456&aaa=789'; const params = new URLSearchParams(qs); console.log(params.toString()); params.sort(); console.log(params.toString()); // 结果 // 执行前 // abc=123&xyz=456&aaa=789&vvv=247 // 执行后 // aaa=789&abc=123&vvv=247&xyz=456

 

 

values,它返回一个 values 迭代器

const {URLSearchParams} = require('url'); const qs = 'abc=123&xyz=456&aaa=789'; const params = new URLSearchParams(qs); for (let v of params.values()) console.log(v); // 结果 /* 789 123 247 456 */

 

 

URLSearchParams 对象本身是可迭代的。

const {URLSearchParams} = require('url'); const qs = 'abc=123&xyz=456&aaa=789'; const params = new URLSearchParams(qs); for (const [k, v] of params) console.log(k, v); // 结果 /* aaa 789 abc 123 vvv 247 xyz 456 */

 

 

概括

我总结了 WHATWG URL API 和 URLSearchParams,它们未来很可能在 URL 解析中发挥重要作用。希望对您有所帮助。
这似乎有助于推进查询字符串的实现,而查询字符串的实现过程出乎意料地繁琐。

 
就这样。

如果您觉得这篇文章有用,请点击【点赞】!
0
加载中...
0票,平均分:0.00/10
4,740
X Facebook Hatena书签 口袋

这篇文章的作者

关于作者

万代洋一

我的主要工作是开发社交游戏的Web API,但我也很荣幸能够从事其他各种工作,包括市场营销。
我在Beyond中的肖像权采用CC0协议。