AST节点的数据结构
AST节点的数据结构
AST(抽象语法树)是源代码的树状表示,Vue3的模板编译器会将模板字符串转换为AST,再进行后续的优化和代码生成。AST节点是构成这棵树的基本单元,每个节点都代表模板中的一个结构元素。
基础节点类型
Vue3的AST节点主要分为以下几种基础类型:
- 根节点(RootNode):整个AST的根,包含模板的所有内容
- 元素节点(ElementNode):对应HTML元素
- 文本节点(TextNode):纯文本内容
- 插值节点(InterpolationNode):双大括号表达式
- 属性节点(AttributeNode):元素的属性
- 指令节点(DirectiveNode):以v-开头的特殊属性
节点通用属性
所有AST节点都继承自基础节点接口,包含以下通用属性:
interface BaseNode {
type: NodeType // 节点类型
loc: SourceLocation // 源代码位置信息
__source?: string // 原始代码字符串(开发模式使用)
}
其中NodeType
是一个枚举,定义了所有可能的节点类型:
const enum NodeType {
ROOT,
ELEMENT,
TEXT,
INTERPOLATION,
ATTRIBUTE,
DIRECTIVE
}
元素节点详解
元素节点是AST中最复杂的节点类型,其数据结构如下:
interface ElementNode extends BaseNode {
type: NodeType.ELEMENT
tag: string // 标签名
props: (AttributeNode | DirectiveNode)[] // 属性/指令数组
children: TemplateChildNode[] // 子节点
isSelfClosing: boolean // 是否自闭合标签
codegenNode?: CodegenNode // 代码生成阶段使用的节点
}
示例:对于模板<div class="container"><span>hello</span></div>
,对应的AST结构大致为:
{
type: NodeType.ELEMENT,
tag: 'div',
props: [
{
type: NodeType.ATTRIBUTE,
name: 'class',
value: {
content: 'container',
loc: { /* 位置信息 */ }
}
}
],
children: [
{
type: NodeType.ELEMENT,
tag: 'span',
props: [],
children: [
{
type: NodeType.TEXT,
content: 'hello',
loc: { /* 位置信息 */ }
}
],
isSelfClosing: false
}
],
isSelfClosing: false
}
属性与指令节点
属性节点和指令节点都用于描述元素的特性,但结构有所不同:
属性节点:
interface AttributeNode extends BaseNode {
type: NodeType.ATTRIBUTE
name: string // 属性名
value: {
content: string // 属性值内容
loc: SourceLocation // 值的位置信息
} | undefined // 可能没有值(如disabled属性)
}
指令节点:
interface DirectiveNode extends BaseNode {
type: NodeType.DIRECTIVE
name: string // 指令名(不含v-前缀)
arg: ExpressionNode | undefined // 参数(如v-on:click中的click)
exp: ExpressionNode | undefined // 表达式(如v-model="message"中的message)
modifiers: string[] // 修饰符数组(如v-model.trim中的trim)
}
示例:<input v-model.trim="message" @click="handleClick">
中的指令节点:
// v-model.trim="message"
{
type: NodeType.DIRECTIVE,
name: 'model',
arg: undefined,
exp: {
type: 'SimpleExpression',
content: 'message',
isStatic: false
},
modifiers: ['trim']
}
// @click="handleClick"
{
type: NodeType.DIRECTIVE,
name: 'on',
arg: {
type: 'SimpleExpression',
content: 'click',
isStatic: true
},
exp: {
type: 'SimpleExpression',
content: 'handleClick',
isStatic: false
},
modifiers: []
}
表达式节点
指令和插值中使用的表达式会被解析为表达式节点:
interface SimpleExpressionNode extends BaseNode {
type: 'SimpleExpression'
content: string // 表达式内容
isStatic: boolean // 是否是静态表达式
constType: ConstantTypes // 常量类型
}
interface CompoundExpressionNode extends BaseNode {
type: 'CompoundExpression'
children: (
| SimpleExpressionNode
| CompoundExpressionNode
| InterpolationNode
| TextNode
| string
)[]
}
示例:{{ count + 1 }}
会被解析为:
{
type: NodeType.INTERPOLATION,
content: {
type: 'SimpleExpression',
content: 'count + 1',
isStatic: false,
constType: 0 // NOT_CONSTANT
}
}
复合表达式
Vue3支持更复杂的表达式结构,如模板字符串和链式调用:
// 模板字符串
`{{ `Hello ${name}` }}`
// 对应的AST节点
{
type: 'CompoundExpression',
children: [
'`Hello ',
{
type: 'SimpleExpression',
content: 'name',
isStatic: false
},
'`'
]
}
静态标记与提升
Vue3的AST节点包含静态分析相关的标记,用于优化:
interface ElementNode extends BaseNode {
// ...
tagType: ElementTypes // 元素类型(组件/原生元素/slot等)
isSelfClosing: boolean
codegenNode?: CodegenNode
ssrCodegenNode?: CodegenNode
// 静态提升相关
staticCount?: number
props?: (AttributeNode | DirectiveNode)[]
patchFlag?: string
dynamicProps?: string[]
// 用于SSR优化
ssrTransform?: boolean
}
示例:静态节点会被标记以便优化:
<div class="static-class">Static content</div>
对应的AST节点会包含:
{
// ...
patchFlag: '1', // 静态标记
staticCount: 1,
// ...
}
源码中的节点创建
Vue3编译器内部通过工厂函数创建节点:
// packages/compiler-core/src/ast.ts
export function createBaseNode(
type: NodeType,
loc: SourceLocation
): BaseNode {
return {
type,
loc,
__source: ''
}
}
export function createElementNode(
tag: string,
props: (AttributeNode | DirectiveNode)[],
children: TemplateChildNode[],
loc: SourceLocation
): ElementNode {
return {
...createBaseNode(NodeType.ELEMENT, loc),
tag,
props,
children,
isSelfClosing: false,
codegenNode: undefined
}
}
节点转换过程
AST节点在编译过程中会经历多次转换:
- 原始AST:由parse函数生成的基础AST
- 转换AST:经过transform处理后的AST
- 代码生成AST:包含代码生成所需信息的最终AST
示例:v-if指令的转换过程:
<div v-if="show">Content</div>
转换后的AST节点会包含条件表达式:
{
type: NodeType.IF,
branches: [
{
condition: {
type: 'SimpleExpression',
content: 'show'
},
children: [/* 原始元素节点 */]
}
]
}
特殊节点处理
一些特殊语法会生成特殊节点结构:
v-for指令:
interface ForNode extends BaseNode {
type: NodeType.FOR
source: ExpressionNode // 迭代对象
value: ExpressionNode | undefined // 值别名
key: ExpressionNode | undefined // 键别名
index: ExpressionNode | undefined // 索引别名
children: TemplateChildNode[] // 循环内容
codegenNode?: ForCodegenNode
}
v-slot指令:
interface SlotOutletNode extends BaseNode {
type: NodeType.SLOT
props: (AttributeNode | DirectiveNode)[]
children: TemplateChildNode[]
codegenNode?: CodegenNode
}
节点位置信息
SourceLocation对象记录了节点在源字符串中的精确位置:
interface SourceLocation {
start: Position // 起始位置
end: Position // 结束位置
source: string // 源字符串片段
}
interface Position {
offset: number // 从0开始的字符偏移量
line: number // 行号(从1开始)
column: number // 列号(从0开始)
}
示例:对于模板<div>hello</div>
,div元素的location可能是:
{
start: { offset: 0, line: 1, column: 0 },
end: { offset: 17, line: 1, column: 17 },
source: '<div>hello</div>'
}
节点辅助工具
Vue3编译器提供了一些工具函数用于操作AST节点:
// 检查节点类型
export function isElementNode(node: any): node is ElementNode {
return node.type === NodeType.ELEMENT
}
// 创建简单的表达式节点
export function createSimpleExpression(
content: string,
isStatic: boolean,
loc = locStub
): SimpleExpressionNode {
return {
type: 'SimpleExpression',
loc,
content,
isStatic,
constType: isStatic ? ConstantTypes.CAN_STRINGIFY : ConstantTypes.NOT_CONSTANT
}
}
// 创建文本节点
export function createTextNode(
content: string,
loc: SourceLocation
): TextNode {
return {
type: NodeType.TEXT,
loc,
content
}
}
节点遍历与访问
编译器使用访问者模式遍历AST节点:
export function traverseNodes(
nodes: TemplateChildNode[],
visitor: NodeVisitor
) {
for (const node of nodes) {
// 调用对应的访问方法
switch (node.type) {
case NodeType.ELEMENT:
visitor.visitElement?.(node)
traverseNodes(node.children, visitor)
break
case NodeType.TEXT:
visitor.visitText?.(node)
break
// 其他节点类型...
}
}
}
示例访问者实现:
const visitor = {
visitElement(node: ElementNode) {
console.log(`访问元素节点: ${node.tag}`)
},
visitText(node: TextNode) {
console.log(`访问文本节点: ${node.content}`)
}
}
节点与代码生成
AST节点最终会被转换为代码生成节点(CodegenNode),这些节点包含生成渲染函数所需的所有信息:
interface VNodeCall {
type: NodeType.ELEMENT
tag: string | symbol
props: PropsExpression | undefined
children: TemplateChildNode[] | undefined
patchFlag: string | undefined
dynamicProps: string | undefined
directives: DirectiveNode[] | undefined
isBlock: boolean
disableTracking: boolean
}
示例:简单的元素节点会被转换为:
{
type: NodeType.ELEMENT,
tag: 'div',
props: {
type: 'ObjectExpression',
properties: [
{
key: { type: 'Identifier', name: 'class' },
value: { type: 'StringLiteral', value: 'container' }
}
]
},
children: [/* 子节点 */],
patchFlag: '1',
isBlock: false
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:模板解析的整体流程