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

大家好,
我是Mandai,Wild团队负责开发工作的成员。
虽然时间有点久远,但Node.js 8.0.0版本于2017年5月30日发布。
从这个版本开始,捆绑了npm 5.0.0版本,并且重写了缓存代码,从而提高了速度。
官方还发布了一条推文,对比了新版本和旧版本的速度。在这个例子中,安装速度似乎只有旧版本的五分之一。
鉴于 #npm5 即将发布,我想更新一下这些基准测试结果。
这是我正在开发的 npm5 代码, [email protected] 与热门仓库中的 pic.twitter.com/KWPfbpE46p
— ✨11x 同性恋 Kat✨ (@maybekatz) 2017 年 5 月 19 日
当前 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
