菜单

解析器和 AST

相关源文件

本文档详细介绍了 Vue.js 编译器使用的解析器架构和抽象语法树(AST)系统。解析器将 Vue 模板代码转换为 AST,提供了一个结构化的表示,后续的编译器阶段可以对其进行转换和操作。有关作用于此 AST 的指令转换信息,请参阅 指令转换

在 Vue.js 编译器中的目的和作用

Vue.js 解析器负责将原始的类 HTML 模板字符串转换为结构化的树状表示。此转换是编译管道中的第一个关键步骤,为所有后续转换奠定了基础。

来源:packages/compiler-core/src/parser.ts packages/compiler-core/src/tokenizer.ts

解析器架构

解析过程的核心包含两个主要组件:

  1. 分词器 (Tokenizer):将模板字符串分解为词元(tokens),识别元素、属性、文本、插值等结构。
  2. 解析器 (Parser):从这些词元构建 AST,创建一个表示模板的分层结构。

来源:packages/compiler-core/src/parser.ts packages/compiler-core/src/tokenizer.ts236-289

关键组件

分词器 (Tokenizer)

Tokenizer 类在 packages/compiler-core/src/tokenizer.ts 中负责通过逐个字符处理输入来将模板分解为词元流。它维护各种状态(在 State 枚举中定义)以根据上下文正确解释输入。

关键状态包括:

  • State.Text:处理正常的文本内容。
  • State.BeforeTagName:在遇到 "<" 字符后。
  • State.InTagName:处理标签名。
  • State.BeforeAttrName:在标签名和其属性之间。
  • State.InAttrName:处理属性名。
  • State.InInterpolation:处理模板插值({{ ... }})。

解析器

解析器主要通过 baseParse 函数和相关辅助函数实现。它使用分词器处理输入,并构建 AST 节点层次结构。解析器处理:

  • 元素嵌套
  • 属性和指令处理
  • 文本和插值处理
  • 错误报告

解析模式 (Parsing Modes)

解析器支持通过 parseMode 选项指定的三种不同模式:

  1. 基础模式 (Base Mode):平台无关的 HTML 类模板解析,统一处理所有标签。
  2. HTML 模式 (HTML Mode):为 <script><style><title><textarea> 等元素添加特殊处理。
  3. SFC 模式 (SFC Mode):针对单文件组件,将所有根级标签(<template> 除外)的内容视为纯文本。

来源:packages/compiler-core/src/options.ts22-44

AST 结构

AST 是由节点组成的树,每个节点代表模板的一部分。节点类型在 NodeTypes 枚举中定义。

来源:packages/compiler-core/src/ast.ts packages/compiler-core/__tests__/parse.spec.ts

节点类型

AST 使用以下主要节点类型:

节点类型描述示例
ROOT包含所有模板内容的根节点。AST 的根节点。
ELEMENTHTML 元素节点。<div><span>
TEXT纯文本内容Hello world
INTERPOLATION模板插值。{{ message }}
COMMENTHTML 注释。<!-- comment -->
ATTRIBUTE元素属性。id="foo"
DIRECTIVEVue 指令。v-if="condition"
IFv-if 块(转换后)。<div v-if="ok">
FORv-for 块(转换后)。<div v-for="item in items">
SIMPLE_EXPRESSIONJavaScript 表达式。item.name

每个节点都包含位置信息(loc),用于跟踪源位置,从而能够提供详细的错误消息。

来源:packages/compiler-core/__tests__/parse.spec.ts13-14

特殊结构

元素节点 (Element Nodes)

元素节点是最复杂的,包含:

元素节点还有一个 tagType 属性,可以进一步将元素归类为以下之一:

  • ElementTypes.ELEMENT:常规 HTML 元素。
  • ElementTypes.COMPONENT:Vue 组件。
  • ElementTypes.SLOT:插槽出口。
  • ElementTypes.TEMPLATE:模板元素。

来源:packages/compiler-core/__tests__/parse.spec.ts444-470

指令节点 (Directive Nodes)

指令节点代表 Vue 指令,如 v-ifv-for 等。

来源:packages/compiler-core/__tests__/parse.spec.ts1223-1236

解析过程

解析过程以 baseParse 函数开始,该函数创建新的分词器并处理模板以构建 AST。

来源:packages/compiler-core/src/parser.ts808-863 packages/compiler-dom/__tests__/parse.spec.ts16-33

关键解析函数

  1. baseParse:初始化解析过程的入口点。
  2. parseChildren:递归解析子节点,直到遇到结束标签或文件末尾。
  3. parseElement:解析元素,包括其属性和子节点。
  4. parseAttributes:解析元素属性。
  5. parseAttribute:解析单个属性。
  6. parseInterpolation:解析模板插值({{ ... }})。

处理特殊情况

解析器包含对某些情况的特殊处理:

  1. 自闭合标签:以 /> 结尾的元素被标记为自闭合。
  2. void 元素:像 <img> 这样不需要闭合标签的元素。
  3. 插值{{ ... }} 中的内容被解析为表达式。
  4. 原生文本元素:像 <script><style><textarea> 等元素的内容被视为原生文本。
  5. v-pre 指令:带有 v-pre 的元素其内容被视为原生文本。

来源:packages/compiler-core/src/parser.ts packages/compiler-dom/__tests__/parse.spec.ts16-33

错误处理

解析器包含强大的错误检测和报告功能,包括:

  • 无效的结束标签
  • 缺失的结束标签
  • 重复的属性
  • 无效的属性值
  • 无效的表达式
  • 缺失的指令参数

错误通过解析器选项中提供的 onError 回调来处理,错误代码在 ErrorCodes 中定义。

来源:packages/compiler-core/src/errors.ts40-108

转换

解析后,AST 会经过各种转换,将它修改为表示更高级别构造的结构。

来源:packages/compiler-core/src/transform.ts packages/compiler-core/src/transforms/vIf.ts packages/compiler-core/src/transforms/vFor.ts

核心转换 (Core Transforms)

主要的转换包括:

  1. v-if/v-else-if/v-else:将这些指令转换为条件分支结构。
  2. v-for:将列表渲染指令转换为列表渲染函数调用。
  3. v-on:转换事件处理器。
  4. v-bind:转换属性绑定。
  5. v-model:转换双向绑定。
  6. v-slot:转换插槽内容。

v-if 转换

v-if 转换将模板结构(例如:

转换为表示条件渲染的专用 AST 结构。

这将在代码生成阶段转换为 JavaScript 条件表达式。

来源: packages/compiler-core/src/transforms/vIf.ts38-76 packages/compiler-core/__tests__/transforms/vIf.spec.ts57-69

v-for 转换

v-for 转换将类似如下的模板结构

转换为表示列表渲染的特定 AST 结构

转换支持多种 v-for 语法,包括:

  • item in items
  • (item, index) in items
  • (value, key, index) in object

来源: packages/compiler-core/src/transforms/vFor.ts53-257 packages/compiler-core/__tests__/transforms/vFor.spec.ts54-72

使用和集成

解析器选项

解析器接受各种选项来控制其行为

来源: packages/compiler-core/src/options.ts22-106

解析器 API

要直接使用解析器

然后可以在代码生成前检查或进一步转换返回的 AST。

结论

Vue.js 的解析器和 AST 系统构成了 Vue 模板编译过程的基础。它们将人类可读的模板语法转换为结构化表示,以便进行分析、转换并最终编译为可执行的 JavaScript。

对于任何研究 Vue 编译器或实现自定义编译时功能的人来说,理解解析器和 AST 结构至关重要。健壮的架构使 Vue 能够支持其丰富的模板语法,同时保持良好的性能和开发者体验。