发布日期:2021 年 7 月 22 日
路由是每个 Web 应用的关键组成部分。从本质上讲,路由会获取网址,对其应用模式匹配或其他应用特有的逻辑,然后通常会根据结果显示网络内容。路由可以通过多种方式实现:
- 将路径映射到磁盘上文件的服务器代码
- 单页应用中的逻辑,用于等待当前位置发生变化,然后创建并显示相应的 DOM 片段。
虽然没有明确的标准,但 Web 开发者已逐渐倾向于使用一种通用语法来表达网址路由模式,该语法与 regular expressions 有很多共同之处,但还包含一些特定于网域的添加项,例如用于匹配路径段的令牌。Express 和 Ruby on Rails 等热门服务器端框架使用此语法(或非常接近的语法),JavaScript 开发者可以使用 path-to-regexp 或 regexpparam 等模块将该逻辑添加到自己的代码中。
URLPattern 是对 Web 平台的补充,它基于这些框架创建的基础构建而成。其目标是标准化路由模式语法,包括支持通配符、命名令牌组、正则表达式组和组修饰符。使用此语法创建的 URLPattern 实例可以执行常见的路由任务,例如与完整网址或网址 pathname 进行匹配,并返回有关令牌和群组匹配的信息。
直接在 Web 平台中提供网址匹配的另一个好处是,这样一来,其他也需要匹配网址的 API 就可以共用一种通用语法。
浏览器支持和填充
在 Chrome 和 Edge 95 及更高版本中,URLPattern 默认处于启用状态。
urlpattern-polyfill 库提供了一种在浏览器或 Node 等缺少内置支持的环境中使用 URLPattern 接口的方法。如果您使用 Polyfill,请务必使用功能检测,以确保仅在当前环境不支持的情况下才加载它。否则,您将无法获享 URLPattern 的一项主要优势:支持环境无需下载和解析额外的代码即可使用它。
if (!(globalThis && 'URLPattern' in globalThis)) {
// URLPattern is not available, so the polyfill is needed.
}
语法兼容性
URLPattern 的指导理念是避免重新发明轮子。如果您已经熟悉 Express 或 Ruby on Rails 中使用的路由语法,则无需学习任何新内容。但鉴于热门路由库中的语法略有不同,必须选择一种作为基本语法,而 URLPattern 的设计者决定使用 path-to-regexp 中的模式语法(但不是其 API 表面)作为起点。
此决定是在与 path-to-regexp 的当前维护者密切协商后做出的。
熟悉支持的语法核心的最佳方式是参阅 path-to-regexp 的文档。您可以前往 GitHub,阅读目前位于 GitHub 上的MDN 发布文档。
其他功能
URLPattern 的语法是 path-to-regexp 支持的语法的超集,因为 URLPattern 支持路由库中不常见的功能:匹配来源,包括主机名中的通配符。大多数其他路由库仅处理网址的 pathname,偶尔也会处理 search 或 hash 部分。它们永远不必检查网址的来源部分,因为它们仅用于自包含 Web 应用中的同源路由。
考虑来源后,便可实现更多用例,例如在 service worker 的 fetch 事件处理程序中路由跨源请求。如果您仅路由同源网址,则可以有效忽略此附加功能,并像使用其他库一样使用 URLPattern。
示例
构建模式
如需创建 URLPattern,请向其构造函数传递字符串或属性包含要匹配的模式相关信息的对象。
传递对象可以最明确地控制用于匹配每个网址组成部分的模式。最详细的日志可能如下所示
const p = new URLPattern({
protocol: 'https',
username: '',
password: '',
hostname: 'example.com',
port: '',
pathname: '/foo/:image.jpg',
search: '*',
hash: '*',
});
如果为属性提供空字符串,则只有在网址的相应部分未设置时才会匹配。通配符 * 将匹配网址指定部分中的任何值。
该构造函数提供了多个快捷方式,以便更轻松地使用。完全省略 search 和 hash 或任何其他属性,相当于将它们设置为 '*' 通配符。该示例可简化为:
const p = new URLPattern({
protocol: 'https',
username: '',
password: '',
hostname: 'example.com',
port: '',
pathname: '/foo/:image.jpg',
});
作为一种额外的快捷方式,您可以在单个属性 baseURL 中提供有关来源的所有信息,从而
const p = new URLPattern({
pathname: '/foo/:image.jpg',
baseURL: 'https://example.com',
});
所有这些示例都假定您的使用情形涉及匹配来源。如果您只希望匹配网址的其他部分(不包括来源,这在许多单来源路由场景中都是如此),则可以完全省略来源信息,只需提供 pathname、search 和 hash 属性的某种组合即可。与之前一样,省略的属性将被视为已设置为 * 通配符模式。
const p = new URLPattern({pathname: '/foo/:image.jpg'});
除了向构造函数传递对象之外,您还可以提供一个或两个字符串。如果提供了一个字符串,则该字符串应表示完整的网址模式,包括用于匹配来源的模式信息。如果您提供两个字符串,则第二个字符串用作 baseURL,第一个字符串被视为相对于该基准。
无论提供的是一个字符串还是两个字符串,URLPattern 构造函数都会解析完整的网址格式,将其分解为网址组成部分,并将较大格式的每个部分映射到相应的组成部分。这意味着,在底层,使用字符串创建的每个 URLPattern 最终都与使用对象创建的等效 URLPattern 具有相同的表示形式。字符串构造函数只是一个快捷方式,适合喜欢简洁界面的用户。
const p = new URLPattern('https://example.com/foo/:image.jpg?*#*');
使用字符串创建 URLPattern 时,请注意以下几点。
使用对象构造 URLPattern 时,如果省略某个属性,则相当于为该属性提供 * 通配符。解析完整的网址字符串格式时,如果某个网址组成部分缺少值,系统会将其视为该组成部分的属性设置为 '',这仅在相应组成部分为空时才会匹配。
使用字符串时,如果您希望在构建的 URLPattern 中使用通配符,则需要明确添加通配符。
// p1 and p2 are equivalent.
const p1 = new URLPattern('/foo', location.origin);
const p2 = new URLPattern({
protocol: location.protocol,
hostname: location.hostname,
pathname: '/foo',
search: '',
hash: '',
});
// p3 and p4 are equivalent.
const p3 = new URLPattern('/foo?*#*', location.origin);
const p4 = new URLPattern({
protocol: location.protocol,
hostname: location.hostname,
pathname: '/foo',
});
您还应注意,将字符串模式解析为其组成部分可能会产生歧义。有些字符(例如 :)既出现在网址中,又在模式匹配语法中具有特殊含义。为避免这种歧义,URLPattern 构造函数假定任何此类特殊字符都是格式的一部分,而不是网址的一部分。如果您希望将不明确的字符解读为网址的一部分,请务必在以字符串形式提供该字符时,使用 \` character. For example, the literal URLabout:blankshould be escaped as“about\:blank”对其进行转义。
使用解锁图案
构建 URLPattern 后,您可以通过两种方式使用它。test() 和 exec() 方法都采用相同的输入,并使用相同的算法来检查是否匹配,只是返回值不同。当给定输入存在匹配项时,test() 返回 true,否则返回 false。
exec() 会返回有关匹配项的详细信息以及捕获组,如果没有匹配项,则返回 null。以下示例演示了如何使用 exec(),但如果您只需要布尔返回值,可以将其中任何一个示例中的 exec() 替换为 test()。
使用 test() 和 exec() 方法的一种方式是传入字符串。与构造函数支持的类似,如果提供的是单个字符串,则该字符串应为完整网址,包括来源。如果提供了两个字符串,则第二个字符串会被视为 baseURL 值,而第一个字符串会根据该基准进行评估。
const p = new URLPattern({
pathname: '/foo/:image.jpg',
baseURL: 'https://example.com',
});
const result = p.exec('https://example.com/foo/cat.jpg');
// result will contain info about the successful match.
// const result = p.exec('/foo/cat.jpg', 'https://example.com')
// is equivalent, using the baseURL syntax.
const noMatchResult = p.exec('https://example.com/bar');
// noMatchResult will be null.
或者,您也可以传递构造函数支持的同类对象,并将属性设置为您希望匹配的网址部分。
const p = new URLPattern({pathname: '/foo/:image.jpg'});
const result = p.exec({pathname: '/foo/:image.jpg'});
// result will contain info about the successful match.
在包含通配符或令牌的 URLPattern 上使用 exec() 时,返回值将提供有关输入网址中相应值的信息。这样一来,您就无需自行解析这些值了。
const p = new URLPattern({
hostname: ':subdomain.example.com',
pathname: '/*/:image.jpg'
});
const result = p.exec('https://imagecdn1.example.com/foo/cat.jpg');
// result.hostname.groups.subdomain will be 'imagecdn1'
// result.pathname.groups[0] will be 'foo', corresponding to *
// result.pathname.groups.image will be 'cat'
匿名群组和命名群组
当您将网址字符串传递给 exec() 时,您会获得一个返回值,该值会告知您哪些部分与模式的所有组匹配。
返回值具有与 URLPattern 的组成部分(例如 pathname)对应的属性。因此,如果某个群组被定义为 URLPattern 的 pathname 部分,则可以在返回值 pathname.groups 中找到匹配项。匹配项的表示方式因相应模式是匿名组还是命名组而有所不同。
您可以使用数组索引来访问匿名模式匹配的值。如果有多个匿名模式,索引 0 表示最左侧模式的匹配值,1 及后续索引用于后续模式。
在模式中使用命名组时,匹配项会作为属性公开,这些属性的名称与每个组名称相对应。
Unicode 支持和规范化
URLPattern 支持 Unicode 字符的方式有多种。
命名群组(例如
:café)可以包含 Unicode 字符。用于有效 JavaScript 标识符的规则适用于命名组。模式中的文本将根据用于对相应组件进行网址编码的相同规则自动进行编码。
pathname中的 Unicode 字符将采用百分比编码,因此pathname模式(例如/café)会自动标准化为/caf%C3%A9。hostname中的 Unicode 字符会自动使用 Punycode 进行编码,而不是使用百分号编码。正则表达式组只能包含 ASCII 字符。正则表达式语法使得自动对这些组中的 Unicode 字符进行编码变得困难且不安全。如果您想在正则表达式组中匹配某个 Unicode 字符,需要手动对其进行百分号编码,例如使用
(caf%C3%A9)匹配café。
除了对 Unicode 字符进行编码之外,URLPattern 还会执行网址规范化。例如,pathname 组件中的 /foo/./bar 会折叠为等效的 /foo/bar。
如果您不确定给定的输入模式是如何归一化的,请使用浏览器的 DevTools 检查构建的 URLPattern 实例。
总结
Glitch 演示展示了在服务工作线程的 fetch event handler 中使用 URLPattern 的核心使用情形,即将特定模式映射到可以生成对网络请求的响应的异步函数。此示例中的概念也可应用于其他路由方案(无论是服务器端还是客户端)。
反馈和未来规划
虽然 URLPattern 的基本功能已在 Chrome 和 Edge 中实现,但我们还计划添加更多功能。URLPattern 的某些方面仍在开发中,并且关于特定行为仍存在许多开放性问题,这些行为可能仍在完善中。我们建议您试用 URLPattern,并通过 GitHub 问题提供任何反馈。
支持模板
path-to-regexp 库提供了一个可有效反转路由行为的 compile() function。compile() 接受模式和令牌占位符的值,并返回一个字符串,其中包含已替换为这些值的网址路径。
我们希望将来能将此功能添加到 网址Pattern 中,但它不在初始版本的范围内。
启用未来的 Web 平台功能
假设 URLPattern 成为 Web 平台的既定组成部分,那么其他可以从路由或模式匹配中受益的功能可以将其作为基元构建在 URLPattern 之上。
目前正在讨论使用 URLPattern 来实现提议的功能,例如 Service Worker 范围模式匹配、将 PWA 用作文件处理程序和推测性预提取。
如需查看完整的致谢名单,请参阅原始说明文档。