← 路由设计 · 稳定入口与可变目标 / URLPattern · 浏览器内建的 URL matcher 待审核 12 / 23

URLPattern · 浏览器内建的 URL matcher

匹配引擎页手写了一台 path → handler 引擎:按 / 切段、逐段对齐、提取 :paramURLPattern 是浏览器把这台引擎标准化、内建的原生 API——而且能力远超手工实现的版本。两点不同最关键:一是匹配范围从「只切 pathname 一段」扩到整条 URL 的每个组成部分(protocol / hostname / port / pathname / search / hash);二是语法从「:param / * 两种」扩到带正则约束的命名组 :id(\d+)、可选分组 {/:slug}?、可重复段 :rest*、枚举 (en|zh)

本页是真的在调用你浏览器里的 window.URLPattern。下面两个 demo 都即时构造一个 new URLPattern(...)、用 test() 判命中、用 exec() 取出每个命名组,改 pattern 或改输入都当场重算。先在 pathname 这一段上看清语法,再把视野放大到整条 URL。

1 · 语法探索器 · 在 pathname 上看清每种 segment

2 · 语法速查

语法 含义
:name 命名组,吃一段(默认不跨 / /users/:id/users/42{ id: "42" }
:name(regex) 给这一段加正则约束 :id(\d+) 只配数字;/users/abc 直接 miss
* 通配,吃掉剩余;是匿名组,组名为字符串 "0" /files/*/files/a/b.png
:name* · :name+ 可重复段(0+ / 1+),把跨段的剩余收进命名组 /files/:rest*{ rest: "a/b.png" }
{…}? 可选分组,默认时该组值为 undefined /posts{/:slug}? 同时配 /posts/posts/x
(a|b) 枚举(其实就是正则约束) /:lang(en|zh)/about 只接受 en / zh

这套语法来自 path-to-regexp 一脉,React Router / Express 用户会很眼熟——区别是 URLPattern 把它写进了 Web 标准

3 · 放大视野 · 不止 pathname,整条 URL 都能写 pattern

手工实现的 matcher 只认 pathname 那一段。URLPattern 的 pattern 是个多组件对象——protocol / hostname / port / pathname / search / hash 各写各的、全部命中才算命中,任一组件没写则默认 *(匹配任意)。下面固定 protocol: "https",你可改 hostname 与 pathname 两条 pattern,看同一批 URL 哪些过、卡在哪个组件上,以及连子域都能作为命名组提取出来。

4 · 真实用法

test() 判命中、exec() 取每个组件的命名组。

// 浏览器内建的 URL matcher —— 不必再手搓 segsOf / matchPattern
const p = new URLPattern({ pathname: '/users/:id(\\d+)' });

p.test({ pathname: '/users/42' });    // → true
p.test({ pathname: '/users/abc' });   // → false   (:id 限定 \\d+)

const m = p.exec({ pathname: '/users/42' });
m.pathname.groups;                    // → { id: '42' }

// 不止 pathname: 整条 URL 的每个组件都能写 pattern, 全部命中才算命中
const api = new URLPattern({
  protocol: 'https',
  hostname: ':sub.example.com',       // 连子域都能作为命名组提取
  pathname: '/api/:version/users/:id(\\d+)',
});
api.exec('https://app.example.com/api/v2/users/42');
// → { hostname: { groups: { sub: 'app' } },
//     pathname: { groups: { version: 'v2', id: '42' } }, ... }

// 也能直接用一整条 URL 字符串构造 (没写的组件默认 * 通配)
new URLPattern('https://*.example.com/api/*');

5 · 它和手工实现的 matcher 差在哪

这件事 手工实现的 matcher(匹配引擎 URLPattern
匹配范围 只切 pathname 一段 整条 URL 七个组件,全部命中才算命中
命名参数 :id 吃一段 :id 一样,但可加正则约束 :id(\d+)
通配 * 吃剩余 * / :rest* / :rest+,还能带前后缀
可选段 不支持——得写两条路由 {/:id}? 内建可选,一条搞定
提取结果 自己拼 params 对象 exec() 给每个组件的 .groups
来源 你维护的几十行代码 浏览器内建 / Web 标准(Node · Deno · Workers 也有)

一句话:匹配引擎页讲的是「这台引擎内部怎么跑」,本页讲的是「浏览器已经把这台引擎做好了,而且更全」。

几处容易踩的。 默认不跨 /:name 只吃一段,要跨段收剩余得用 *:name*匿名组叫 "0"* 的捕获结果在 groups["0"],不是某个名字。JS 字符串里 \ 要转义:在源码里写 :id(\\d+) 才得到正则 \d+(本页输入框里直接写一个 \ 即可,因为读的是 input.value)。是 full match:pattern 默认匹配整个组件,不是子串搜索;没写的组件默认 *,容易「以为限制了其实没限制」。

6 · 相关页

  • 本系列 · 路由匹配引擎——先看手写版怎么按 / 切段、逐段对齐、提取 :param,以及「顺序即优先级」「specificity 排序」「radix tree」三条路——本页是它的标准化、整条 URL 版。
  • 本系列 · Navigation API——同属「为路由重做的现代浏览器原生接口」:Navigation API 接住导航事件,URLPattern 负责匹配——两者常一起搭出新一代 client router。
  • URL Anatomy · 一条网址怎么拆开——URLPattern 的多组件(protocol / hostname / port / pathname / search / hash)正好对应一条 URL 的各块,那个系列逐段拆开讲。
  • MDN · URLPattern API——构造器(字符串 / URLPatternInit 两种)、test() / exec()、各组件语法与浏览器兼容性的权威说明。
  • WHATWG · URLPattern Standard——规范本体:pattern 如何解析成各组件、命名组 / 正则组 / 通配的精确语义。