Appearance
5. 数据结构:Token 的设计与实现
在上一章中,我们深入剖析了驱动 parse 函数的核心正则表达式。我们知道,这个正则表达式的目的是在路径字符串中识别出“有意义的片段”。现在,我们需要为这些“片段”定义一个清晰、规范的计算机表示——这便是 Token 数据结构。
Token 是 path-to-regexp 中最重要的概念之一。它是连接“词法分析”和“代码生成”两个阶段的桥梁。一个设计良好的 Token 结构,将使我们的后续代码变得异常清晰和简单。
5.1. Token 的类型
从 parse 函数的视角来看,路径字符串可以被分解为两种最基本的 Token:
- 静态路径 (Path): 路径中不变的、纯文本的部分,例如
/user/或/posts。 - 参数 (Parameter): 路径中动态的部分,用于捕获变量,例如
:id。
因此,我们的 Token 类型至少需要能够区分这两者。在 TypeScript 中,我们可以定义一个 Token 类型,它是一个联合类型(Union Type),可以是代表静态路径的 string,也可以是代表参数的 Key 对象。
typescript
// A token is a string (for static parts) or a Key object (for dynamic parts).
export type Token = string | Key;这种设计的精妙之处在于它的简洁性。对于最常见的静态路径,我们直接使用 string 类型来表示,既直观又高效。只有在遇到更复杂的参数时,我们才需要动用一个专门的对象结构 Key 来描述它。
5.2. Key 接口的设计
Key 接口是 Token 结构的核心,它需要能够承载一个动态参数的所有信息。根据我们在第二章学习的路径语法,一个参数拥有以下属性:
- 名称 (name): 参数的名字,如
id。 - 前缀 (prefix): 参数前面的分隔符,通常是
/或.。 - 后缀 (suffix): 参数后面的可选字符,通常用于闭合括号等情况。
- 匹配模式 (pattern): 参数的自定义正则表达式,如
\d+。 - 修饰符 (modifier): 参数的量词,如
?,*,+。
基于这些需求,我们可以定义出 Key 接口的完整结构:
typescript
// Represents a single dynamic path segment.
export interface Key {
name: string | number; // 参数名,对于未命名参数可以是数字
prefix: string | null; // 前缀,如 / 或 .
suffix: string | null; // 后缀,如 )
pattern: string; // 参数的匹配模式 (正则表达式字符串)
modifier: "?" | "*" | "+" | ""; // 修饰符
}让我们来逐一解析这个接口的每个字段:
name: string | number: 参数的名称。对于命名参数:id,它就是字符串"id"。对于未命名参数(\d+),path-to-regexp会为其分配一个从 0 开始的数字索引,所以类型是number。prefix: string | null: 参数的前缀。对于路径/user/:id,:id这个Key的prefix就是/。对于根路径/下的参数/:id,prefix也是/。如果参数没有前缀(例如在路径的开头),则为null。suffix: string | null: 参数的后缀。这个字段通常用于处理更复杂的模式,例如带括号的参数组。在大多数常见情况下,它为null。pattern: string: 这是该参数对应的正则表达式片段。对于:id,它的默认值是[^\/]+?(匹配一个或多个非斜杠字符,非贪婪模式)。对于:id(\d+),它的值就是\d+。modifier: "?" | "*" | "+" | "": 参数的修饰符。""表示没有修饰符,即参数是必需的、只出现一次的。
5.3. Token 数组示例
有了 string 和 Key 的定义,我们现在可以更精确地描述 parse 函数的输出了。让我们再次审视 /user/:id(\d+)? 这个例子:
Input:
/user/:id(\d+)?Output (
Token[]):json[ "/user", { "name": "id", "prefix": "/", "suffix": null, "pattern": "\\d+", "modifier": "?" } ]
这个 Token 数组完美地、结构化地描述了原始路径:
- 第一个
Token是一个string"/user",代表静态路径部分。 - 第二个
Token是一个Key对象,它告诉我们:- 这是一个名为
id的参数。 - 它的前缀是
/。 - 它要求匹配一个或多个数字 (
\d+)。 - 它是可选的 (
?)。
- 这是一个名为
这个 Token 数组就是我们的“抽象语法树”(AST)。它非常简单,就是一个扁平的数组,但它已经包含了后续“代码生成”(无论是生成 RegExp 还是生成 toPath 函数)所需要的所有信息。
在下一章,我们将把理论付诸实践,利用上一章的 PATH_REGEXP 和本章的 Token 结构,完整地实现 parse 函数。我们将看到,parse 函数的实现过程,就是一个消费正则表达式匹配结果,并精心构造 Key 对象的过程。