网址Pattern 为网络平台引入了路由

一种用于标准化常见模式匹配用例的方法。

背景

路由是每个 Web 应用的重要组成部分。从本质上讲,路由涉及获取网址、对其应用某种模式匹配或其他应用专用逻辑,然后通常根据结果显示 Web 内容。路由可以通过多种方式实现:有时是服务器上运行的代码,用于将路径映射到磁盘上的文件;有时是单页应用中的逻辑,用于等待当前位置发生变化并创建要显示的相应 DOM 部分。

虽然没有统一的标准,但 Web 开发者倾向于使用一种通用语法来表达网址路由模式,这种模式与 regular expressions 有许多共同之处,但也有一些特定于领域的附加内容,例如用于匹配路径段的令牌。ExpressRuby on Rails 等流行的服务器端框架使用此语法(或非常接近此语法的语法),JavaScript 开发者可以使用 path-to-regexpregexpparam 等模块将此逻辑添加到自己的代码中。

URLPattern 是 Web 平台的补充,它基于这些框架创建的基础构建而成。其目标是标准化路由模式语法,包括支持通配符、命名令牌组、正则表达式组和组修饰符。使用此语法创建的 URLPattern 实例可以执行常见的路由任务,例如与完整网址或网址 pathname 进行匹配,以及返回有关令牌和组匹配的信息。

直接在 Web 平台中提供网址匹配的另一个好处是,您可以与也需要与网址匹配的其他 API 共享通用语法。

浏览器支持和 polyfill

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 上阅读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: '*',
});

仅当网址的对应部分未设置时,为属性提供空字符串才会匹配。通配符 * 将与网址的给定部分的任何值匹配。

该构造函数提供了多个快捷方式,以便更轻松地使用。完全省略 searchhash 或任何其他属性,相当于将它们设置为 '*' 通配符。上述示例可简化为

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',
});

所有这些示例都假定您的用例涉及匹配的来源。如果您只对网址的其他部分(不包括来源)进行匹配(许多“传统”单源路由场景就是如此),则可以完全省略来源信息,只提供 pathnamesearchhash 属性的某种组合。与之前一样,系统会将省略的属性视为设置为 * 通配符模式。

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,否则返回 falseexec() 会返回有关匹配项的详细信息以及捕获组,如果没有匹配项,则返回 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)对应的属性。因此,如果某个组被定义为 URLPatternpathname 部分,则可以在返回值的 pathname.groups 中找到匹配项。匹配项的表示方式因相应模式是匿名组还是命名组而异。

您可以使用数组索引来访问匿名模式匹配的值。如果有多个匿名模式,索引 0 将表示最左侧模式的匹配值,1 和其他索引将用于后续模式。

在模式中使用命名组时,匹配项将显示为名称与每个组名称相对应的属性。

Unicode 支持和标准化

URLPattern 支持多种不同的 Unicode 字符。

  • 命名组(例如 :café)可以包含 Unicode 字符。适用于有效 JavaScript 标识符的规则也适用于命名组。

  • 格式中的文本将根据用于对该特定组件进行网址编码的规则自动编码。pathname 中的 Unicode 字符将采用百分比编码,因此 /cafépathname 模式会自动标准化为 /caf%C3%A9hostname 中的 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,但这不在初始版本的范围内。

启用未来的网站平台功能

假设 URLPattern 成为 Web 平台的既定部分,那么其他可能受益于路由或模式匹配的功能可以作为基元在此之上构建。

我们正在讨论如何使用 URLPattern 实现一些提议的功能,例如服务工件作用域模式匹配将 PWA 用作文件处理程序推测性预提取

致谢

如需查看完整的致谢名单,请参阅原始说明文档