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

大家好,
我是Mandai,Wild团队负责开发工作的成员。

虽然时间有点久远,但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 解析器(公钥)的官方支持。

这次,我们将研究 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,800
X Facebook Hatena书签 口袋

这篇文章的作者

关于作者

万代洋一

我的主要工作是开发社交游戏的Web API,但幸运的是,我也有机会参与其他各种任务,包括市场营销。
我在Beyond中的图像版权采用CC0协议。