本文档详细介绍了 Vue.js 编译器使用的解析器架构和抽象语法树(AST)系统。解析器将 Vue 模板代码转换为 AST,提供了一个结构化的表示,后续的编译器阶段可以对其进行转换和操作。有关作用于此 AST 的指令转换信息,请参阅 指令转换。
Vue.js 解析器负责将原始的类 HTML 模板字符串转换为结构化的树状表示。此转换是编译管道中的第一个关键步骤,为所有后续转换奠定了基础。
来源:packages/compiler-core/src/parser.ts packages/compiler-core/src/tokenizer.ts
解析过程的核心包含两个主要组件:
来源:packages/compiler-core/src/parser.ts packages/compiler-core/src/tokenizer.ts236-289
Tokenizer 类在 packages/compiler-core/src/tokenizer.ts 中负责通过逐个字符处理输入来将模板分解为词元流。它维护各种状态(在 State 枚举中定义)以根据上下文正确解释输入。
关键状态包括:
State.Text:处理正常的文本内容。State.BeforeTagName:在遇到 "<" 字符后。State.InTagName:处理标签名。State.BeforeAttrName:在标签名和其属性之间。State.InAttrName:处理属性名。State.InInterpolation:处理模板插值({{ ... }})。解析器主要通过 baseParse 函数和相关辅助函数实现。它使用分词器处理输入,并构建 AST 节点层次结构。解析器处理:
解析器支持通过 parseMode 选项指定的三种不同模式:
<script>、<style>、<title> 和 <textarea> 等元素添加特殊处理。<template> 除外)的内容视为纯文本。来源:packages/compiler-core/src/options.ts22-44
AST 是由节点组成的树,每个节点代表模板的一部分。节点类型在 NodeTypes 枚举中定义。
来源:packages/compiler-core/src/ast.ts packages/compiler-core/__tests__/parse.spec.ts
AST 使用以下主要节点类型:
| 节点类型 | 描述 | 示例 |
|---|---|---|
ROOT | 包含所有模板内容的根节点。 | AST 的根节点。 |
ELEMENT | HTML 元素节点。 | <div>、<span> |
TEXT | 纯文本内容 | Hello world |
INTERPOLATION | 模板插值。 | {{ message }} |
COMMENT | HTML 注释。 | <!-- comment --> |
ATTRIBUTE | 元素属性。 | id="foo" |
DIRECTIVE | Vue 指令。 | v-if="condition" |
IF | v-if 块(转换后)。 | <div v-if="ok"> |
FOR | v-for 块(转换后)。 | <div v-for="item in items"> |
SIMPLE_EXPRESSION | JavaScript 表达式。 | item.name |
每个节点都包含位置信息(loc),用于跟踪源位置,从而能够提供详细的错误消息。
来源:packages/compiler-core/__tests__/parse.spec.ts13-14
元素节点是最复杂的,包含:
元素节点还有一个 tagType 属性,可以进一步将元素归类为以下之一:
ElementTypes.ELEMENT:常规 HTML 元素。ElementTypes.COMPONENT:Vue 组件。ElementTypes.SLOT:插槽出口。ElementTypes.TEMPLATE:模板元素。来源:packages/compiler-core/__tests__/parse.spec.ts444-470
指令节点代表 Vue 指令,如 v-if、v-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
baseParse:初始化解析过程的入口点。parseChildren:递归解析子节点,直到遇到结束标签或文件末尾。parseElement:解析元素,包括其属性和子节点。parseAttributes:解析元素属性。parseAttribute:解析单个属性。parseInterpolation:解析模板插值({{ ... }})。解析器包含对某些情况的特殊处理:
/> 结尾的元素被标记为自闭合。<img> 这样不需要闭合标签的元素。{{ ... }} 中的内容被解析为表达式。<script>、<style> 和 <textarea> 等元素的内容被视为原生文本。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
主要的转换包括:
v-if 转换将模板结构(例如:
转换为表示条件渲染的专用 AST 结构。
这将在代码生成阶段转换为 JavaScript 条件表达式。
来源: packages/compiler-core/src/transforms/vIf.ts38-76 packages/compiler-core/__tests__/transforms/vIf.spec.ts57-69
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
要直接使用解析器
然后可以在代码生成前检查或进一步转换返回的 AST。
Vue.js 的解析器和 AST 系统构成了 Vue 模板编译过程的基础。它们将人类可读的模板语法转换为结构化表示,以便进行分析、转换并最终编译为可执行的 JavaScript。
对于任何研究 Vue 编译器或实现自定义编译时功能的人来说,理解解析器和 AST 结构至关重要。健壮的架构使 Vue 能够支持其丰富的模板语法,同时保持良好的性能和开发者体验。