一种标准化常见模式匹配用例的方法。
背景
路由是每个 Web 应用的重要组成部分。从本质上讲,路由涉及获取网址、对其应用某种模式匹配或其他应用专用逻辑,然后通常根据结果显示 Web 内容。路由可以通过多种方式实现:有时是服务器上运行的代码,用于将路径映射到磁盘上的文件;有时是单页应用中的逻辑,用于等待当前位置发生变化,并创建要显示的相应 DOM 部分。
虽然没有统一的标准,但 Web 开发者倾向于使用一种通用语法来表达网址路由模式,这种模式与 regular expressions
有许多共同之处,但也有一些特定于领域的附加内容,例如用于匹配路径段的令牌。Express 和 Ruby on Rails 等流行的服务器端框架使用此语法(或非常接近此语法的语法),JavaScript 开发者可以使用 path-to-regexp
或 regexpparam
等模块将此逻辑添加到自己的代码中。
URLPattern
是对 Web 平台的补充,基于这些框架创建的基础构建。其目标是标准化路由模式语法,包括支持通配符、命名令牌组、正则表达式组和组修饰符。使用此语法创建的 URLPattern
实例可以执行常见的路由任务,例如与完整网址或网址 pathname
进行匹配,以及返回有关令牌和组匹配的信息。
直接在 Web 平台中提供网址匹配的另一个好处是,您可以与也需要与网址匹配的其他 API 共享通用语法。
浏览器支持和 polyfill
Chrome 和 Edge 95 及更高版本中默认启用 URLPattern
。
urlpattern-polyfill
库提供了一种在浏览器或缺少内置支持的 Node 等环境中使用 URLPattern
接口的方法。如果使用 polyfill,请务必使用特征检测,以确保仅在当前环境不支持时才会加载 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 Surface)作为起点。
此决定是在与 path-to-regexp
的现任维护者密切协商后做出的。
如需熟悉支持的语法的核心,最好的方法是参阅 path-to-regexp
的文档。您可以在 GitHub 上阅读MDN 上发布的文档。
其他功能
URLPattern
的语法是 path-to-regexp
支持语法的超集,因为 URLPattern
支持路由库中不常见的功能:匹配源,包括主机名中的通配符。大多数其他路由库只处理pathname,偶尔也处理网址的搜索或哈希部分。它们无需检查网址的来源部分,因为它们仅用于在自包含 Web 应用中进行同源路由。
考虑来源有助于实现更多用例,例如在服务工件的 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 URL
about:blankshould be escaped as
'about\:blank'` 对其进行转义。
使用模式
构建 URLPattern
后,您可以通过两种方式使用它。test()
和 exec()
方法采用相同的输入,并使用相同的算法检查是否匹配,仅在返回值方面有所不同。如果有与给定输入匹配的项,test()
会返回 true
,否则返回 false
。exec()
会返回有关匹配项的详细信息以及捕获组,如果没有匹配项,则返回 null
。以下示例演示了使用 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 字符将采用百分比编码,因此/café
等pathname
模式会自动标准化为/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
实现一些提议的功能,例如服务工件作用域模式匹配、将 PWA 用作文件处理程序和推测性预提取。
致谢
如需查看完整的致谢名单,请参阅原始说明文档。