URLPattern · 浏览器内建的 URL matcher
匹配引擎手写了一台 path 到 handler 的引擎:按 / 切段、逐段对齐、提取 :param。URLPattern 是浏览器把这台引擎标准化、内建的原生 API,能力远超手工版本。
两点不同最关键。一是匹配范围:从只切 pathname 一段,扩到整条 URL 的八个组件——protocol、username、password、hostname、port、pathname、search、hash,全部命中才算命中。二是语法:从 :param 与
* 两种,扩到带正则约束的命名组 :id(\d+)、可选分组 {/:slug}?、可重复段 :rest* 与枚举 (en|zh)。
本页两个 lab 都在真实调用浏览器里的 window.URLPattern,即时构造 new URLPattern(...)、用 test() 判命中、用 exec() 取出命名组。它已进入各大浏览器:Chrome 与 Edge 95+、Firefox 142+(2025-08)、Safari 26+(2025-09),Deno、Bun 与 Workers 侧也都有(兼容性据 MDN
BCD,核对于 2026-08)。
1 · pathname 上的语法
test() 的判定与 exec() 取出的命名组。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 |
3 · 整条 URL 都能写 pattern
手工实现的 matcher 只认 pathname 那一段。URLPattern 的 pattern 是个多组件对象,八个组件各写各的、全部命中才算命中,任一组件没写则默认 * 匹配任意。日常用到的通常是 protocol、hostname、port、pathname、search、hash
这六个,username 与 password 对应 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 | URLPattern |
|---|---|---|
| 匹配范围 | 只切 pathname 一段 | 整条 URL 八个组件,全部命中才算命中 |
| 命名参数 | :id 吃一段 |
:id 一样,但可加正则约束 :id(\d+) |
| 通配 | * 吃剩余 |
* / :rest* / :rest+,还能带前后缀 |
| 可选段 | 不支持——得写两条路由 | {/:id}? 内建可选,一条搞定 |
| 提取结果 | 自己拼 params 对象 | exec() 给每个组件的 .groups |
| 来源 | 自己维护的几十行代码 | 浏览器内建的 Web 标准 |
警示 · 四处容易踩的地方,下列判定均在 Chrome 151 上实测(核对于 2026-08)。:name 默认不跨 /,只吃一段,要跨段收剩余得用 * 或 :name*——new URLPattern({pathname:'/users/:id'}) 对 /users/a/b 返回
false。* 的捕获结果是匿名组,落在 groups["0"] 而不是某个名字。JS 源码里写 :id(\\d+) 才得到正则 \d+,本页输入框里写一个反斜杠即可,因为读的是 input.value。最后,pattern 是 full match 而非子串搜索,{pathname:'/users'}
对 /users/42 返回 false;而没写的组件默认 *,容易误以为限制住了而实际没有。
Navigation API 接住导航事件、URLPattern 负责匹配,两者常一起搭出新一代 client router,见 Navigation API。URLPattern 的多组件正好对应一条 URL 的各块,URL Anatomy 系列逐段拆开讲。