Skip to content
On this page

5. 数据结构:Token 的设计与实现

在上一章中,我们深入剖析了驱动 parse 函数的核心正则表达式。我们知道,这个正则表达式的目的是在路径字符串中识别出“有意义的片段”。现在,我们需要为这些“片段”定义一个清晰、规范的计算机表示——这便是 Token 数据结构。

Tokenpath-to-regexp 中最重要的概念之一。它是连接“词法分析”和“代码生成”两个阶段的桥梁。一个设计良好的 Token 结构,将使我们的后续代码变得异常清晰和简单。

5.1. Token 的类型

parse 函数的视角来看,路径字符串可以被分解为两种最基本的 Token

  1. 静态路径 (Path): 路径中不变的、纯文本的部分,例如 /user//posts
  2. 参数 (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 这个 Keyprefix 就是 /。对于根路径 / 下的参数 /:idprefix 也是 /。如果参数没有前缀(例如在路径的开头),则为 null

  • suffix: string | null: 参数的后缀。这个字段通常用于处理更复杂的模式,例如带括号的参数组。在大多数常见情况下,它为 null

  • pattern: string: 这是该参数对应的正则表达式片段。对于 :id,它的默认值是 [^\/]+?(匹配一个或多个非斜杠字符,非贪婪模式)。对于 :id(\d+),它的值就是 \d+

  • modifier: "?" | "*" | "+" | "": 参数的修饰符。"" 表示没有修饰符,即参数是必需的、只出现一次的。

5.3. Token 数组示例

有了 stringKey 的定义,我们现在可以更精确地描述 parse 函数的输出了。让我们再次审视 /user/:id(\d+)? 这个例子:

  • Input: /user/:id(\d+)?

  • Output (Token[]):

    json
    [
      "/user",
      {
        "name": "id",
        "prefix": "/",
        "suffix": null,
        "pattern": "\\d+",
        "modifier": "?"
      }
    ]

这个 Token 数组完美地、结构化地描述了原始路径:

  1. 第一个 Token 是一个 string "/user",代表静态路径部分。
  2. 第二个 Token 是一个 Key 对象,它告诉我们:
    • 这是一个名为 id 的参数。
    • 它的前缀是 /
    • 它要求匹配一个或多个数字 (\d+)。
    • 它是可选的 (?)。

这个 Token 数组就是我们的“抽象语法树”(AST)。它非常简单,就是一个扁平的数组,但它已经包含了后续“代码生成”(无论是生成 RegExp 还是生成 toPath 函数)所需要的所有信息。

在下一章,我们将把理论付诸实践,利用上一章的 PATH_REGEXP 和本章的 Token 结构,完整地实现 parse 函数。我们将看到,parse 函数的实现过程,就是一个消费正则表达式匹配结果,并精心构造 Key 对象的过程。

5. 数据结构:Token 的设计与实现 has loaded