Appearance
4.2 AST 转换:transform 阶段对 AST 做了哪些优化?
AST转换是Vue 3编译器的核心阶段,负责将解析得到的原始AST转换为优化的、可用于代码生成的AST。本节将深入分析Vue 3编译器的转换系统,探讨其插件化架构、核心优化策略以及各种转换器的实现原理。
4.2.1 转换系统架构概览
核心组件关系
Vue 3的转换系统采用了高度模块化的插件架构:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ transform() │───▶│ TransformContext│───▶│ NodeTransform │
│ 主转换函数 │ │ 转换上下文 │ │ 节点转换器 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ traverseNode() │ │ DirectiveTransform│ │ StructuralTransform│
│ 节点遍历 │ │ 指令转换器 │ │ 结构指令转换 │
└─────────────────┘ └─────────────────┘ └─────────────────┘转换流程概述
- 初始化阶段:创建转换上下文,配置转换选项
- 遍历阶段:深度优先遍历AST,应用节点转换器
- 优化阶段:静态提升、缓存优化、代码生成节点创建
- 后处理阶段:收集辅助函数、组件、指令等元信息
4.2.2 核心转换函数:transform()
主转换流程
中的 函数是整个转换过程的入口:typescript
export function transform(root: RootNode, options: TransformOptions): void {
// 1. 创建转换上下文
const context = createTransformContext(root, options)
// 2. 遍历并转换AST节点
traverseNode(root, context)
// 3. 静态提升优化
if (options.hoistStatic) {
cacheStatic(root, context)
}
// 4. 创建根节点代码生成节点
if (!options.ssr) {
createRootCodegen(root, context)
}
// 5. 收集元信息
root.helpers = new Set([...context.helpers.keys()])
root.components = [...context.components]
root.directives = [...context.directives]
root.imports = context.imports
root.hoists = context.hoists
root.temps = context.temps
root.cached = context.cached
root.transformed = true
}转换上下文设计
是转换过程中的核心数据结构:typescript
export interface TransformContext {
// 基础配置
selfName: string | null
root: RootNode
filename: string
// 收集的元信息
helpers: Map<symbol, number> // 运行时辅助函数
components: Set<string> // 组件列表
directives: Set<string> // 指令列表
hoists: (JSChildNode | null)[] // 静态提升节点
imports: ImportItem[] // 导入项
temps: number // 临时变量计数
cached: (CacheExpression | null)[] // 缓存表达式
// 作用域管理
identifiers: { [name: string]: number | undefined }
scopes: {
vFor: number // v-for作用域计数
vSlot: number // v-slot作用域计数
vPre: number // v-pre作用域计数
vOnce: number // v-once作用域计数
}
// 遍历状态
parent: ParentNode | null
grandParent: ParentNode | null
childIndex: number
currentNode: RootNode | TemplateChildNode | null
inVOnce: boolean
// 工具方法
helper<T extends symbol>(name: T): T
removeHelper<T extends symbol>(name: T): void
helperString(name: symbol): string
replaceNode(node: TemplateChildNode): void
removeNode(node?: TemplateChildNode): void
onNodeRemoved(): void
addIdentifiers(exp: ExpressionNode | string): void
removeIdentifiers(exp: ExpressionNode | string): void
hoist(exp: string | JSChildNode | ArrayExpression): SimpleExpressionNode
cache(exp: JSChildNode, isVNode?: boolean, inVOnce?: boolean): CacheExpression
// 常量类型缓存
constantCache: WeakMap<TemplateChildNode, ConstantTypes>
}4.2.3 节点遍历机制
深度优先遍历
实现了深度优先的AST遍历:typescript
export function traverseNode(
node: RootNode | TemplateChildNode,
context: TransformContext,
): void {
context.currentNode = node
// 1. 应用节点转换器(进入阶段)
const { nodeTransforms } = context
const exitFns = []
for (let i = 0; i < nodeTransforms.length; i++) {
const onExit = nodeTransforms[i](node, context)
if (onExit) {
if (isArray(onExit)) {
exitFns.push(...onExit)
} else {
exitFns.push(onExit)
}
}
// 节点可能被移除或替换
if (!context.currentNode) {
return // 节点被移除
} else {
node = context.currentNode // 节点可能被替换
}
}
// 2. 根据节点类型进行特殊处理
switch (node.type) {
case NodeTypes.COMMENT:
if (!context.ssr) {
context.helper(CREATE_COMMENT)
}
break
case NodeTypes.INTERPOLATION:
if (!context.ssr) {
context.helper(TO_DISPLAY_STRING)
}
break
case NodeTypes.IF:
for (let i = 0; i < node.branches.length; i++) {
traverseNode(node.branches[i], context)
}
break
case NodeTypes.IF_BRANCH:
case NodeTypes.FOR:
case NodeTypes.ELEMENT:
case NodeTypes.ROOT:
traverseChildren(node, context)
break
}
// 3. 执行退出回调(退出阶段)
context.currentNode = node
let i = exitFns.length
while (i--) {
exitFns[i]()
}
}转换器类型体系
typescript
// 节点转换器:处理AST节点
export type NodeTransform = (
node: RootNode | TemplateChildNode,
context: TransformContext,
) => void | (() => void) | (() => void)[]
// 指令转换器:处理指令属性
export type DirectiveTransform = (
dir: DirectiveNode,
node: ElementNode,
context: TransformContext,
augmentor?: (ret: DirectiveTransformResult) => DirectiveTransformResult,
) => DirectiveTransformResult
// 结构指令转换器:处理v-if、v-for等结构指令
export type StructuralDirectiveTransform = (
node: ElementNode,
dir: DirectiveNode,
context: TransformContext,
) => void | (() => void)4.2.4 核心转换器实现
元素转换器:transformElement
中的 负责将元素节点转换为VNode调用:typescript
export const transformElement: NodeTransform = (node, context) => {
// 在退出阶段执行,确保所有子表达式已处理
return function postTransformElement() {
node = context.currentNode!
if (!(
node.type === NodeTypes.ELEMENT &&
(node.tagType === ElementTypes.ELEMENT ||
node.tagType === ElementTypes.COMPONENT)
)) {
return
}
const { tag, props } = node
const isComponent = node.tagType === ElementTypes.COMPONENT
// 1. 解析组件类型或标签名
let vnodeTag = isComponent
? resolveComponentType(node as ComponentNode, context)
: `"${tag}"`
const isDynamicComponent =
isObject(vnodeTag) && vnodeTag.callee === RESOLVE_DYNAMIC_COMPONENT
// 2. 构建属性表达式
let vnodeProps: PropsExpression | undefined
let vnodeChildren: TemplateChildNode[] | undefined
let vnodePatchFlag: number = 0
let patchFlag: number = 0
let vnodeDynamicProps: string[] | undefined
let dynamicPropNames: string[]
let vnodeDirectives: DirectiveNode[] | undefined
let shouldUseBlock =
// dynamic component may resolve to plain elements
isDynamicComponent ||
vnodeTag === SUSPENSE ||
vnodeTag === TELEPORT ||
vnodeTag === KEEP_ALIVE ||
(!isComponent &&
// <svg> and <foreignObject> must be forced into blocks so that block
// updates inside get proper isSVG flag at runtime. (#639, #643)
// This is technically web-specific, but splitting the logic out of core
// leads to too much unnecessary complexity.
(tag === 'svg' || tag === 'foreignObject'))
// 3. 处理属性和指令
if (props.length > 0) {
const propsBuildResult = buildProps(
node,
context,
props,
isComponent,
isDynamicComponent,
)
vnodeProps = propsBuildResult.props
patchFlag = propsBuildResult.patchFlag
dynamicPropNames = propsBuildResult.dynamicPropNames
vnodeDirectives = propsBuildResult.directives
shouldUseBlock = shouldUseBlock || propsBuildResult.shouldUseBlock
}
// 4. 处理子节点
if (node.children.length > 0) {
if (vnodeTag === KEEP_ALIVE) {
// KeepAlive特殊处理
shouldUseBlock = true
patchFlag |= PatchFlags.DYNAMIC_SLOTS
}
if (isComponent && vnodeTag !== TELEPORT && vnodeTag !== KEEP_ALIVE) {
// 组件子节点处理
const { slots, hasDynamicSlots } = buildSlots(node, context)
vnodeChildren = slots
if (hasDynamicSlots) {
patchFlag |= PatchFlags.DYNAMIC_SLOTS
}
} else if (node.children.length === 1 && vnodeTag !== TELEPORT) {
// 单子节点优化
const child = node.children[0]
const type = child.type
const hasDynamicTextChild =
type === NodeTypes.INTERPOLATION ||
type === NodeTypes.COMPOUND_EXPRESSION
if (hasDynamicTextChild && getConstantType(child, context) === ConstantTypes.NOT_CONSTANT) {
patchFlag |= PatchFlags.TEXT
}
if (hasDynamicTextChild || type === NodeTypes.TEXT) {
vnodeChildren = child
} else {
vnodeChildren = node.children
}
} else {
vnodeChildren = node.children
}
}
// 5. 处理patch flag
if (patchFlag !== 0) {
if (__DEV__) {
if (patchFlag < 0) {
vnodePatchFlag = patchFlag + ` /* ${PatchFlagNames[patchFlag]} */`
} else {
const flagNames = Object.keys(PatchFlagNames)
.map(Number)
.filter(n => n > 0 && patchFlag & n)
.map(n => PatchFlagNames[n])
.join(`, `)
vnodePatchFlag = patchFlag + ` /* ${flagNames} */`
}
} else {
vnodePatchFlag = patchFlag
}
if (dynamicPropNames && dynamicPropNames.length) {
vnodeDynamicProps = stringifyDynamicPropNames(dynamicPropNames)
}
}
// 6. 创建VNode调用节点
node.codegenNode = createVNodeCall(
context,
vnodeTag,
vnodeProps,
vnodeChildren,
vnodePatchFlag,
vnodeDynamicProps,
vnodeDirectives,
!!shouldUseBlock,
false /* disableTracking */,
isComponent,
node.loc,
)
}
}文本优化器:transformText
中的 负责合并相邻文本节点:typescript
export const transformText: NodeTransform = (node, context) => {
if (
node.type === NodeTypes.ROOT ||
node.type === NodeTypes.ELEMENT ||
node.type === NodeTypes.FOR ||
node.type === NodeTypes.IF_BRANCH
) {
// 在退出阶段执行,确保所有表达式已处理
return () => {
const children = node.children
let currentContainer: CompoundExpressionNode | undefined = undefined
let hasText = false
// 1. 合并相邻文本节点
for (let i = 0; i < children.length; i++) {
const child = children[i]
if (isText(child)) {
hasText = true
for (let j = i + 1; j < children.length; j++) {
const next = children[j]
if (isText(next)) {
if (!currentContainer) {
// 创建复合表达式容器
currentContainer = children[i] = createCompoundExpression(
[child],
child.loc,
)
}
// 合并相邻文本节点
currentContainer.children.push(` + `, next)
children.splice(j, 1)
j--
} else {
currentContainer = undefined
break
}
}
}
}
// 2. 优化单文本子节点
if (
!hasText ||
(children.length === 1 &&
(node.type === NodeTypes.ROOT ||
(node.type === NodeTypes.ELEMENT &&
node.tagType === ElementTypes.ELEMENT &&
!node.props.find(
p =>
p.type === NodeTypes.DIRECTIVE &&
!context.directiveTransforms[p.name],
))))
) {
return
}
// 3. 预转换文本节点为createTextVNode调用
for (let i = 0; i < children.length; i++) {
const child = children[i]
if (isText(child) || child.type === NodeTypes.COMPOUND_EXPRESSION) {
const callArgs: CallExpression['arguments'] = []
// createTextVNode默认为单空格,优化空格情况
if (child.type !== NodeTypes.TEXT || child.content !== ' ') {
callArgs.push(child)
}
// 标记动态文本以便在block中打补丁
if (
!context.ssr &&
getConstantType(child, context) === ConstantTypes.NOT_CONSTANT
) {
callArgs.push(
PatchFlags.TEXT + (__DEV__ ? ` /* ${PatchFlagNames[PatchFlags.TEXT]} */` : ``),
)
}
children[i] = {
type: NodeTypes.TEXT_CALL,
content: child,
loc: child.loc,
codegenNode: createCallExpression(
context.helper(CREATE_TEXT),
callArgs,
),
}
}
}
}
}
}4.2.5 结构指令转换
条件渲染转换:transformIf
中的 处理v-if指令:typescript
export const transformIf: NodeTransform = createStructuralDirectiveTransform(
/^(?:if|else|else-if)$/,
(node, dir, context) => {
return processIf(node, dir, context, (ifNode, branch, isRoot) => {
// 动态计算key值,基于兄弟节点
const siblings = context.parent!.children
let i = siblings.indexOf(ifNode)
let key = 0
while (i-- >= 0) {
const sibling = siblings[i]
if (sibling && sibling.type === NodeTypes.IF) {
key += sibling.branches.length
}
}
// 退出回调:完成代码生成节点创建
return () => {
if (isRoot) {
ifNode.codegenNode = createCodegenNodeForBranch(
branch,
key,
context,
) as IfConditionalExpression
} else {
// 将分支代码生成节点附加到v-if根节点
const parentCondition = getParentCondition(ifNode.codegenNode!)
parentCondition.alternate = createCodegenNodeForBranch(
branch,
key + ifNode.branches.length - 1,
context,
)
}
}
})
},
)
export function processIf(
node: ElementNode,
dir: DirectiveNode,
context: TransformContext,
processCodegen?: (
node: IfNode,
branch: IfBranchNode,
isRoot: boolean,
) => (() => void) | undefined,
): (() => void) | undefined {
// 1. 验证表达式
if (
dir.name !== 'else' &&
(!dir.exp || !(dir.exp as SimpleExpressionNode).content.trim())
) {
const loc = dir.exp ? dir.exp.loc : node.loc
context.onError(
createCompilerError(ErrorCodes.X_V_IF_NO_EXPRESSION, dir.loc),
)
dir.exp = createSimpleExpression(`true`, false, loc)
}
// 2. 处理表达式(非浏览器环境)
if (!__BROWSER__ && context.prefixIdentifiers && dir.exp) {
dir.exp = processExpression(dir.exp as SimpleExpressionNode, context)
}
// 3. 创建或扩展IF节点
if (dir.name === 'if') {
const branch = createIfBranch(node, dir)
const ifNode: IfNode = {
type: NodeTypes.IF,
loc: node.loc,
branches: [branch],
}
context.replaceNode(ifNode)
if (processCodegen) {
return processCodegen(ifNode, branch, true)
}
} else {
// v-else(-if)
const siblings = context.parent!.children
const comments = []
let i = siblings.indexOf(node)
while (i-- >= -1) {
const sibling = siblings[i]
if (sibling && sibling.type === NodeTypes.COMMENT) {
context.removeNode(sibling)
comments.unshift(sibling)
continue
}
if (
sibling &&
sibling.type === NodeTypes.TEXT &&
!sibling.content.trim().length
) {
context.removeNode(sibling)
continue
}
if (sibling && sibling.type === NodeTypes.IF) {
// 移除当前节点并添加到IF节点的分支
context.removeNode()
const branch = createIfBranch(node, dir)
if (comments.length && !__BROWSER__) {
branch.children = comments.concat(branch.children)
}
// 检查else-if条件
if (dir.name === 'else-if') {
sibling.branches.push(branch)
} else {
sibling.branches.push(branch)
}
const parentCondition = getParentCondition(sibling.codegenNode!)
parentCondition.alternate = processCodegen
? processCodegen(sibling, branch, false)
: undefined
} else {
context.onError(
createCompilerError(ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, node.loc),
)
}
break
}
}
}列表渲染转换:transformFor
中的 处理v-for指令:typescript
export const transformFor: NodeTransform = createStructuralDirectiveTransform(
'for',
(node, dir, context) => {
const { helper, removeHelper } = context
return processFor(node, dir, context, forNode => {
// 1. 创建循环渲染函数表达式
const renderExp = createCallExpression(helper(RENDER_LIST), [
forNode.source,
]) as ForRenderListExpression
const isTemplate = isTemplateNode(node)
const memo = findDir(node, 'memo')
const keyProp = findProp(node, `key`, false, true)
const isDirKey = keyProp && keyProp.type === NodeTypes.DIRECTIVE
// 2. 处理:key简写
if (isDirKey && !keyProp.exp) {
transformBindShorthand(keyProp, context)
}
// 3. 解析key表达式
let keyExp =
keyProp &&
(keyProp.type === NodeTypes.ATTRIBUTE
? keyProp.value
? createSimpleExpression(keyProp.value.content, true)
: undefined
: keyProp.exp)
// 4. 处理v-memo和key表达式
if (memo && keyExp && isDirKey) {
if (!__BROWSER__) {
keyProp.exp = keyExp = processExpression(
keyExp as SimpleExpressionNode,
context,
)
}
}
const keyProperty =
keyProp && keyExp ? createObjectProperty(`key`, keyExp) : null
// 5. 处理template v-for的特殊情况
if (!__BROWSER__ && isTemplate) {
if (memo) {
memo.exp = processExpression(
memo.exp! as SimpleExpressionNode,
context,
)
}
if (keyProp && isDirKey && keyProp.exp) {
keyProp.exp = processExpression(
keyProp.exp as SimpleExpressionNode,
context,
)
}
}
const isStableFragment =
forNode.source.type === NodeTypes.SIMPLE_EXPRESSION &&
forNode.source.constType > ConstantTypes.NOT_CONSTANT
const fragmentFlag = isStableFragment
? PatchFlags.STABLE_FRAGMENT
: keyProp
? PatchFlags.KEYED_FRAGMENT
: PatchFlags.UNKEYED_FRAGMENT
// 6. 创建for循环代码生成节点
forNode.codegenNode = createVNodeCall(
context,
helper(FRAGMENT),
undefined,
renderExp,
fragmentFlag +
(__DEV__ ? ` /* ${PatchFlagNames[fragmentFlag]} */` : ``),
undefined,
undefined,
true /* isBlock */,
!isStableFragment /* disableTracking */,
false /* isComponent */,
node.loc,
)
return () => {
// 7. 完成渲染函数参数
let childBlock: BlockCodegenNode
const { children } = forNode
// 检查template v-for
const needFragmentWrapper =
children.length !== 1 || children[0].type !== NodeTypes.ELEMENT
const slotOutlet = isSlotOutlet(node)
? node
: isTemplate &&
node.children.length === 1 &&
isSlotOutlet(node.children[0])
? (node.children[0] as SlotOutletNode)
: null
if (slotOutlet) {
// slot outlet特殊处理
childBlock = slotOutlet.codegenNode as RenderSlotCall
if (isTemplate && keyProperty) {
injectProp(childBlock, keyProperty, context)
}
} else if (needFragmentWrapper) {
// 需要fragment包装
childBlock = createVNodeCall(
context,
helper(FRAGMENT),
keyProperty ? createObjectExpression([keyProperty]) : undefined,
node.children,
PatchFlags.STABLE_FRAGMENT +
(__DEV__
? ` /* ${PatchFlagNames[PatchFlags.STABLE_FRAGMENT]} */`
: ``),
undefined,
undefined,
true,
undefined,
false /* isComponent */,
)
} else {
// 单元素子节点
childBlock = (children[0] as PlainElementNode)
.codegenNode as VNodeCall
if (isTemplate && keyProperty) {
injectProp(childBlock, keyProperty, context)
}
if (childBlock.isBlock !== !isStableFragment) {
if (childBlock.isBlock) {
removeHelper(OPEN_BLOCK)
removeHelper(
getVNodeBlockHelper(context.inSSR, childBlock.isComponent),
)
} else {
removeHelper(
getVNodeHelper(context.inSSR, childBlock.isComponent),
)
}
}
childBlock.isBlock = !isStableFragment
if (childBlock.isBlock) {
helper(OPEN_BLOCK)
helper(getVNodeBlockHelper(context.inSSR, childBlock.isComponent))
} else {
helper(getVNodeHelper(context.inSSR, childBlock.isComponent))
}
}
// 8. 处理v-memo
if (memo) {
const loop = createFunctionExpression(
createForLoopParams(forNode.parseResult, [
createSimpleExpression(`_cached`),
]),
)
loop.body = createBlockStatement([
createSimpleExpression(`const _memo = (${memo.exp!.content})`),
createSimpleExpression(
`if (_cached && ${context.helperString(
IS_MEMO_SAME,
)}(_cached, _memo)) return _cached`,
),
createSimpleExpression(`const _item = ${childBlock.type === NodeTypes.VNODE_CALL ? `${context.helperString(WITH_MEMO)}(` : ``}`),
childBlock as ForCodegenNode,
createSimpleExpression(
childBlock.type === NodeTypes.VNODE_CALL ? `)` : ``,
),
createSimpleExpression(`_item.memo = _memo`),
createSimpleExpression(`return _item`),
])
renderExp.arguments.push(
loop as ForIteratorExpression,
createSimpleExpression(`_cache`),
createSimpleExpression(String(context.cached.length)),
)
context.cached.push(null)
} else {
renderExp.arguments.push(
createFunctionExpression(
createForLoopParams(forNode.parseResult),
childBlock,
true /* force newline */,
) as ForIteratorExpression,
)
}
}
})
},
)4.2.6 静态提升优化
静态分析与提升
中的 实现静态提升优化:typescript
export function cacheStatic(root: RootNode, context: TransformContext): void {
walk(
root,
undefined,
context,
// 根节点由于潜在的父级fallthrough属性而无法提升
!!getSingleElementRoot(root),
)
}
function walk(
node: ParentNode,
parent: ParentNode | undefined,
context: TransformContext,
doNotHoistNode: boolean = false,
inFor = false,
) {
const { children } = node
const toCache: (PlainElementNode | TextCallNode)[] = []
for (let i = 0; i < children.length; i++) {
const child = children[i]
// 只有普通元素和文本调用可以缓存
if (
child.type === NodeTypes.ELEMENT &&
child.tagType === ElementTypes.ELEMENT
) {
const constantType = doNotHoistNode
? ConstantTypes.NOT_CONSTANT
: getConstantType(child, context)
if (constantType > ConstantTypes.NOT_CONSTANT) {
if (constantType >= ConstantTypes.CAN_CACHE) {
// 标记为可缓存
;(child.codegenNode as VNodeCall).patchFlag = PatchFlags.CACHED
toCache.push(child)
continue
}
} else {
// 节点可能包含动态子节点,但其属性可能可以提升
const codegenNode = child.codegenNode!
if (codegenNode.type === NodeTypes.VNODE_CALL) {
const flag = codegenNode.patchFlag
if (
(flag === undefined ||
flag === PatchFlags.NEED_PATCH ||
flag === PatchFlags.TEXT) &&
getGeneratedPropsConstantType(child, context) >=
ConstantTypes.CAN_CACHE
) {
const props = getNodeProps(child)
if (props) {
codegenNode.props = context.hoist(props)
}
}
if (codegenNode.dynamicProps) {
codegenNode.dynamicProps = context.hoist(codegenNode.dynamicProps)
}
}
}
} else if (
child.type === NodeTypes.TEXT_CALL &&
getConstantType(child.content, context) >= ConstantTypes.CAN_CACHE
) {
toCache.push(child)
}
// 递归处理子节点
if (child.type === NodeTypes.ELEMENT) {
const isComponent = child.tagType === ElementTypes.COMPONENT
if (isComponent) {
context.scopes.vSlot++
}
walk(child, node, context, false, inFor)
if (isComponent) {
context.scopes.vSlot--
}
} else if (child.type === NodeTypes.FOR) {
walk(child, node, context, false, true)
} else if (child.type === NodeTypes.IF) {
for (let i = 0; i < child.branches.length; i++) {
walk(
child.branches[i],
node,
context,
false,
inFor,
)
}
}
}
// 批量缓存静态节点
if (toCache.length && !inFor) {
for (const child of toCache) {
child.codegenNode = context.cache(child.codegenNode!)
}
}
}常量类型分析
typescript
export function getConstantType(
node: TemplateChildNode | SimpleExpressionNode | CacheExpression,
context: TransformContext,
): ConstantTypes {
const { constantCache } = context
// 检查缓存
switch (node.type) {
case NodeTypes.ELEMENT:
if (node.tagType !== ElementTypes.ELEMENT) {
return ConstantTypes.NOT_CONSTANT
}
const cached = constantCache.get(node)
if (cached !== undefined) {
return cached
}
const codegenNode = node.codegenNode!
if (codegenNode.type !== NodeTypes.VNODE_CALL) {
return ConstantTypes.NOT_CONSTANT
}
if (
codegenNode.isBlock &&
node.tag !== 'svg' &&
node.tag !== 'foreignObject'
) {
return ConstantTypes.NOT_CONSTANT
}
const flag = codegenNode.patchFlag
if (!flag) {
let returnType = ConstantTypes.CAN_STRINGIFY
// 检查属性
const generatedPropsType = getGeneratedPropsConstantType(node, context)
if (generatedPropsType === ConstantTypes.NOT_CONSTANT) {
constantCache.set(node, ConstantTypes.NOT_CONSTANT)
return ConstantTypes.NOT_CONSTANT
}
if (generatedPropsType < returnType) {
returnType = generatedPropsType
}
// 检查子节点
for (let i = 0; i < node.children.length; i++) {
const childType = getConstantType(node.children[i], context)
if (childType === ConstantTypes.NOT_CONSTANT) {
constantCache.set(node, ConstantTypes.NOT_CONSTANT)
return ConstantTypes.NOT_CONSTANT
}
if (childType < returnType) {
returnType = childType
}
}
// 只有当所有子节点都可以字符串化时,才能字符串化
if (returnType > ConstantTypes.CAN_CACHE) {
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i]
if (
child.type === NodeTypes.ELEMENT &&
getConstantType(child, context) === ConstantTypes.CAN_CACHE
) {
returnType = ConstantTypes.CAN_CACHE
break
}
}
}
constantCache.set(node, returnType)
return returnType
} else {
constantCache.set(node, ConstantTypes.NOT_CONSTANT)
return ConstantTypes.NOT_CONSTANT
}
case NodeTypes.TEXT:
case NodeTypes.COMMENT:
return ConstantTypes.CAN_STRINGIFY
case NodeTypes.IF:
case NodeTypes.FOR:
case NodeTypes.IF_BRANCH:
return ConstantTypes.NOT_CONSTANT
case NodeTypes.INTERPOLATION:
case NodeTypes.TEXT_CALL:
return getConstantType(node.content, context)
case NodeTypes.SIMPLE_EXPRESSION:
return node.constType
case NodeTypes.COMPOUND_EXPRESSION:
let returnType = ConstantTypes.CAN_STRINGIFY
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i]
if (isString(child) || isSymbol(child)) {
continue
}
const childType = getConstantType(child, context)
if (childType === ConstantTypes.NOT_CONSTANT) {
return ConstantTypes.NOT_CONSTANT
} else if (childType < returnType) {
returnType = childType
}
}
return returnType
default:
if (__DEV__) {
const exhaustiveCheck: never = node
exhaustiveCheck
}
return ConstantTypes.NOT_CONSTANT
}
}4.2.7 指令转换系统
指令转换器注册
typescript
// 内置指令转换器映射
const DOMDirectiveTransforms: Record<string, DirectiveTransform> = {
bind: transformBind,
cloak: noopDirectiveTransform,
html: transformVHtml,
text: transformVText,
model: transformModel, // 覆盖compiler-core
on: transformOn, // 覆盖compiler-core
show: transformShow,
}
// 核心指令转换器
const directiveTransforms: Record<string, DirectiveTransform> = {
bind: transformBind,
on: transformOn,
model: transformModel,
memo: transformMemo,
once: transformOnce,
slot: transformSlot,
}v-bind转换器
typescript
export const transformBind: DirectiveTransform = (dir, _node, context) => {
const { exp, modifiers, loc } = dir
const arg = dir.arg!
if (arg.type !== NodeTypes.SIMPLE_EXPRESSION) {
arg.children.unshift(`(`)
arg.children.push(`) || ""`)
} else if (!arg.isStatic) {
arg.content = `${arg.content} || ""`
}
// .sync转换为v-model
if (modifiers.includes('sync')) {
if (__COMPAT__) {
// Vue 2兼容性警告
context.onWarn(
createCompilerError(
CompilerDeprecationTypes.COMPILER_V_BIND_SYNC,
loc,
),
)
return {
props: [],
needRuntime: context.helper(V_MODEL_DYNAMIC),
}
} else {
context.onError(
createCompilerError(ErrorCodes.X_V_BIND_SYNC_MODIFIER_NOT_SUPPORTED, loc),
)
}
}
// .prop修饰符
if (modifiers.includes('prop')) {
injectPrefix(arg, '.')
}
// .attr修饰符
if (modifiers.includes('attr')) {
injectPrefix(arg, '^')
}
if (
!exp ||
(exp.type === NodeTypes.SIMPLE_EXPRESSION && !exp.content.trim())
) {
context.onError(
createCompilerError(ErrorCodes.X_V_BIND_NO_EXPRESSION, loc),
)
return {
props: [createObjectProperty(arg, createSimpleExpression('', true, loc))],
}
}
return {
props: [createObjectProperty(arg, exp)],
}
}v-on转换器
typescript
export const transformOn: DirectiveTransform = (
dir,
node,
context,
augmentor,
) => {
const { loc, modifiers, arg } = dir
if (!dir.exp && !modifiers.length) {
context.onError(createCompilerError(ErrorCodes.X_V_ON_NO_EXPRESSION, loc))
}
let eventName: ExpressionNode
if (arg.type === NodeTypes.SIMPLE_EXPRESSION) {
if (arg.isStatic) {
let rawName = arg.content
// 处理@vnode-xxx事件
if (rawName.startsWith('vnode')) {
context.onWarn(
createCompilerError(ErrorCodes.X_V_ON_VNODE_HOOKS, loc),
)
}
// 2.x $emit('click') -> 3.x @click
// 2.x $emit('update:value') -> 3.x @update:value
eventName = createSimpleExpression(
toHandlerKey(camelize(rawName)),
true,
arg.loc,
)
} else {
// 动态事件名
eventName = createCompoundExpression([
`${context.helperString(TO_HANDLER_KEY)}(`,
arg,
`)`,
])
}
} else {
// 已经是复合表达式
eventName = arg
eventName.children.unshift(`${context.helperString(TO_HANDLER_KEY)}(`)
eventName.children.push(`)`)
}
// 处理事件处理器
let exp: ExpressionNode | undefined = dir.exp as
| SimpleExpressionNode
| undefined
if (exp && !__BROWSER__) {
exp = processExpression(exp, context, false, context.cacheHandlers)
}
let shouldCache: boolean = context.cacheHandlers && !exp && !context.inVOnce
if (exp) {
const isMemberExp = isMemberExpression(exp.content)
const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content))
const hasMultipleStatements = exp.content.includes(`;`)
if (__DEV__ && __BROWSER__) {
validateBrowserExpression(
exp as SimpleExpressionNode,
context,
false,
hasMultipleStatements,
)
}
if (isInlineStatement || (shouldCache && isMemberExp)) {
// 包装内联语句为函数表达式
exp = createCompoundExpression([
`${isInlineStatement ? `$event` : `${``}(...args)`} => ${hasMultipleStatements ? `{` : `(`}`,
exp,
hasMultipleStatements ? `}` : `)`,
])
shouldCache = false
}
}
let ret: DirectiveTransformResult = {
props: [
createObjectProperty(
eventName,
exp || createSimpleExpression(`() => {}`, false, loc),
),
],
}
// 应用扩展器(如果有)
if (augmentor) {
ret = augmentor(ret)
}
if (shouldCache) {
ret.props[0].value = context.cache(ret.props[0].value)
}
// 标记需要运行时
ret.props.forEach(p => (p.key.isHandlerKey = true))
return ret
}4.2.8 代码生成节点创建
VNode调用节点
typescript
export function createVNodeCall(
context: TransformContext | null,
tag: VNodeCall['tag'],
props?: VNodeCall['props'],
children?: VNodeCall['children'],
patchFlag?: VNodeCall['patchFlag'],
dynamicProps?: VNodeCall['dynamicProps'],
directives?: VNodeCall['directives'],
isBlock: VNodeCall['isBlock'] = false,
disableTracking: VNodeCall['disableTracking'] = false,
isComponent: VNodeCall['isComponent'] = false,
loc = locStub,
): VNodeCall {
if (context) {
if (isBlock) {
context.helper(OPEN_BLOCK)
context.helper(getVNodeBlockHelper(context.inSSR, isComponent))
} else {
context.helper(getVNodeHelper(context.inSSR, isComponent))
}
if (directives) {
context.helper(WITH_DIRECTIVES)
}
}
return {
type: NodeTypes.VNODE_CALL,
tag,
props,
children,
patchFlag,
dynamicProps,
directives,
isBlock,
disableTracking,
isComponent,
loc,
}
}根节点代码生成
typescript
function createRootCodegen(root: RootNode, context: TransformContext) {
const { helper } = context
const { children } = root
if (children.length === 1) {
const singleElementRootChild = getSingleElementRoot(root)
// 如果单个子元素是元素,将其转换为block
if (singleElementRootChild && singleElementRootChild.codegenNode) {
const codegenNode = singleElementRootChild.codegenNode
if (codegenNode.type === NodeTypes.VNODE_CALL) {
convertToBlock(codegenNode, context)
}
root.codegenNode = codegenNode
} else {
// 单个<slot/>、IfNode、ForNode:已经是blocks
// 单个文本节点:总是打补丁
// 根代码生成通过genNode()传递
root.codegenNode = children[0]
}
} else if (children.length > 1) {
// 根有多个节点 - 返回fragment block
let patchFlag = PatchFlags.STABLE_FRAGMENT
// 检查fragment是否实际包含单个有效子节点,其余为注释
if (
__DEV__ &&
children.filter(c => c.type !== NodeTypes.COMMENT).length === 1
) {
patchFlag |= PatchFlags.DEV_ROOT_FRAGMENT
}
root.codegenNode = createVNodeCall(
context,
helper(FRAGMENT),
undefined,
root.children,
patchFlag,
undefined,
undefined,
true,
undefined,
false /* isComponent */,
)
} else {
// 没有子节点 = noop。代码生成将返回null
}
}4.2.9 性能优化策略
Patch Flag系统
typescript
// Patch Flag枚举
export enum PatchFlags {
TEXT = 1, // 动态文本内容
CLASS = 1 << 1, // 动态class
STYLE = 1 << 2, // 动态style
PROPS = 1 << 3, // 动态属性,不包括class和style
FULL_PROPS = 1 << 4, // 具有动态key的属性,需要完整diff
HYDRATE_EVENTS = 1 << 5, // 仅用于hydration的事件监听器
STABLE_FRAGMENT = 1 << 6, // 稳定fragment,子节点顺序不变
KEYED_FRAGMENT = 1 << 7, // 带key的fragment
UNKEYED_FRAGMENT = 1 << 8, // 无key的fragment
NEED_PATCH = 1 << 9, // 需要patch,用于非属性比较
DYNAMIC_SLOTS = 1 << 10, // 动态slot
DEV_ROOT_FRAGMENT = 1 << 11, // 开发环境根fragment标记
CACHED = -1, // 静态缓存
BAIL = -2, // diff算法应该退出优化模式
}
// Patch Flag名称映射(开发环境)
export const PatchFlagNames: Record<PatchFlags, string> = {
[PatchFlags.TEXT]: `TEXT`,
[PatchFlags.CLASS]: `CLASS`,
[PatchFlags.STYLE]: `STYLE`,
[PatchFlags.PROPS]: `PROPS`,
[PatchFlags.FULL_PROPS]: `FULL_PROPS`,
[PatchFlags.HYDRATE_EVENTS]: `HYDRATE_EVENTS`,
[PatchFlags.STABLE_FRAGMENT]: `STABLE_FRAGMENT`,
[PatchFlags.KEYED_FRAGMENT]: `KEYED_FRAGMENT`,
[PatchFlags.UNKEYED_FRAGMENT]: `UNKEYED_FRAGMENT`,
[PatchFlags.NEED_PATCH]: `NEED_PATCH`,
[PatchFlags.DYNAMIC_SLOTS]: `DYNAMIC_SLOTS`,
[PatchFlags.DEV_ROOT_FRAGMENT]: `DEV_ROOT_FRAGMENT`,
[PatchFlags.CACHED]: `CACHED`,
[PatchFlags.BAIL]: `BAIL`,
}动态属性收集
typescript
function buildProps(
node: ElementNode,
context: TransformContext,
props: ElementNode['props'] | undefined = node.props,
isComponent: boolean,
isDynamicComponent: boolean,
ssr = false,
): {
props: PropsExpression | undefined
directives: DirectiveNode[]
patchFlag: number
dynamicPropNames: string[]
shouldUseBlock: boolean
} {
const { tag, loc: elementLoc, children } = node
let properties: Property[] = []
const mergeArgs: PropsExpression[] = []
const runtimeDirectives: DirectiveNode[] = []
let patchFlag = 0
let hasRef = false
let hasClassBinding = false
let hasStyleBinding = false
let hasHydrationEventBinding = false
let hasDynamicKeys = false
let hasVnodeHook = false
const dynamicPropNames: string[] = []
const analyzePatchFlag = (key: Property['key']) => {
if (isStaticExp(key)) {
const name = key.content
const isEventHandler = isOn(name)
if (isEventHandler && (!isComponent || isDynamicComponent)) {
// 事件监听器标记
if (name.toLowerCase() !== 'onclick' && name !== 'onUpdate:modelValue' && !isReservedProp(name)) {
hasHydrationEventBinding = true
}
}
if (isEventHandler && isReservedProp(name)) {
hasVnodeHook = true
}
if (
name === 'ref' ||
name === 'ref_for' ||
name === 'ref_key'
) {
hasRef = true
} else if (name === 'class') {
hasClassBinding = true
} else if (name === 'style') {
hasStyleBinding = true
} else if (name !== 'key' && !dynamicPropNames.includes(name)) {
dynamicPropNames.push(name)
}
// 处理组件v-model
if (
isComponent &&
(name === 'class' || name === 'style') &&
!dynamicPropNames.includes(name)
) {
dynamicPropNames.push(name)
}
} else {
hasDynamicKeys = true
}
}
// 处理属性和指令
for (let i = 0; i < props.length; i++) {
const prop = props[i]
if (prop.type === NodeTypes.ATTRIBUTE) {
const { loc, name, value } = prop
let isStatic = true
if (name === 'ref') {
hasRef = true
if (context.scopes.vFor > 0) {
properties.push(
createObjectProperty(
createSimpleExpression('ref_for', true),
createSimpleExpression('true'),
),
)
}
}
// 跳过is属性(在vIf.ts中处理)
if (name === 'is' && (isComponentTag(tag) || (value && value.content.startsWith('vue:')))) {
continue
}
properties.push(
createObjectProperty(
createSimpleExpression(name, true, getInnerRange(loc, 0, name.length)),
createSimpleExpression(
value ? value.content : '',
isStatic,
value ? value.loc : loc,
),
),
)
} else {
// 指令
const { name, arg, exp, loc, modifiers } = prop
const isVBind = name === 'bind'
const isVOn = name === 'on'
// 跳过v-slot - 在vSlot.ts中处理
if (name === 'slot') {
continue
}
// 特殊指令处理
if (name === 'once' || name === 'memo') {
continue
}
if (isVBind && isStaticArgOf(arg, 'is')) {
continue
}
const directiveTransform = context.directiveTransforms[name]
if (directiveTransform) {
// 内置指令转换
const { props: dirProps, needRuntime } = directiveTransform(
prop,
node,
context,
)
!ssr && dirProps.forEach(analyzePatchFlag)
if (isVOn && arg && !isStaticExp(arg)) {
mergeArgs.push(createObjectExpression(dirProps, elementLoc))
} else {
properties.push(...dirProps)
}
if (needRuntime) {
runtimeDirectives.push(prop)
if (isSymbol(needRuntime)) {
directiveImportMap.set(prop, needRuntime)
}
}
} else if (!isBuiltInDirective(name)) {
// 用户自定义指令
runtimeDirectives.push(prop)
if (hasChildren) {
shouldUseBlock = true
}
}
}
}
let propsExpression: PropsExpression | undefined = undefined
// 设置patch flags
if (mergeArgs.length) {
if (properties.length) {
mergeArgs.unshift(createObjectExpression(properties, elementLoc))
}
if (mergeArgs.length > 1) {
propsExpression = createCallExpression(
context.helper(MERGE_PROPS),
mergeArgs,
elementLoc,
)
} else {
propsExpression = mergeArgs[0]
}
} else if (properties.length) {
propsExpression = createObjectExpression(properties, elementLoc)
}
// patch flag分析
if (hasDynamicKeys) {
patchFlag |= PatchFlags.FULL_PROPS
} else {
if (hasClassBinding && !isComponent) {
patchFlag |= PatchFlags.CLASS
}
if (hasStyleBinding && !isComponent) {
patchFlag |= PatchFlags.STYLE
}
if (dynamicPropNames.length) {
patchFlag |= PatchFlags.PROPS
}
if (hasHydrationEventBinding) {
patchFlag |= PatchFlags.HYDRATE_EVENTS
}
}
if (
(patchFlag === 0 || patchFlag === PatchFlags.HYDRATE_EVENTS) &&
(hasRef || hasVnodeHook || runtimeDirectives.length > 0)
) {
patchFlag |= PatchFlags.NEED_PATCH
}
// 检查是否需要使用block
if (!shouldUseBlock && patchFlag !== 0) {
shouldUseBlock =
patchFlag & PatchFlags.FULL_PROPS ||
patchFlag & PatchFlags.HYDRATE_EVENTS ||
runtimeDirectives.length > 0
}
return {
props: propsExpression,
directives: runtimeDirectives,
patchFlag,
dynamicPropNames,
shouldUseBlock,
}
}4.2.10 转换插件扩展机制
插件注册与配置
typescript
// 转换选项接口
export interface TransformOptions {
// 节点转换器
nodeTransforms?: NodeTransform[]
// 指令转换器
directiveTransforms?: Record<string, DirectiveTransform | undefined>
// 转换上下文选项
transformHoist?: HoistTransform | null
isBuiltInComponent?: (tag: string) => symbol | void
isCustomElement?: (tag: string) => boolean | void
expressionPlugins?: ParserPlugin[]
scopeId?: string | null
slotted?: boolean
ssr?: boolean
inSSR?: boolean
ssrCssVars?: string
bindingMetadata?: BindingMetadata
inline?: boolean
isTS?: boolean
onError?: (error: CompilerError) => void
onWarn?: (warning: CompilerError) => void
compatConfig?: CompilerCompatConfig
// 优化选项
hoistStatic?: boolean
cacheHandlers?: boolean
nodeTransforms?: NodeTransform[]
directiveTransforms?: Record<string, DirectiveTransform>
prefixIdentifiers?: boolean
// 开发选项
filename?: string
sourceMap?: boolean
// 实验性选项
experimentalFeatures?: CompilerFeatureFlags
}
// 默认转换器配置
export function getBaseTransformPreset(
prefixIdentifiers?: boolean,
): TransformPreset {
return [
[
transformOnce,
transformIf,
transformMemo,
transformFor,
...(__COMPAT__ ? [transformFilter] : []),
...(!__BROWSER__ && prefixIdentifiers
? [
// 顺序很重要
trackVForSlotScopes,
transformExpression,
]
: __BROWSER__ && __DEV__
? [transformExpression]
: []),
transformSlotOutlet,
transformElement,
trackSlotScopes,
transformText,
],
{
on: transformOn,
bind: transformBind,
model: transformModel,
},
]
}自定义转换器开发
typescript
// 自定义节点转换器示例
const customNodeTransform: NodeTransform = (node, context) => {
if (node.type === NodeTypes.ELEMENT && node.tag === 'custom-component') {
// 在进入阶段处理
console.log('Processing custom component:', node)
// 返回退出回调
return () => {
// 在退出阶段处理
console.log('Finished processing custom component:', node)
// 修改节点
if (node.codegenNode) {
// 添加自定义属性
const props = node.codegenNode.props
if (props && props.type === NodeTypes.JS_OBJECT_EXPRESSION) {
props.properties.push(
createObjectProperty(
createSimpleExpression('data-custom', true),
createSimpleExpression('true', true),
),
)
}
}
}
}
}
// 自定义指令转换器示例
const customDirectiveTransform: DirectiveTransform = (dir, node, context) => {
const { exp, arg, modifiers } = dir
// 验证指令
if (!exp) {
context.onError(
createCompilerError(
ErrorCodes.X_V_CUSTOM_NO_EXPRESSION,
dir.loc,
),
)
return { props: [] }
}
// 处理修饰符
const modifierProps = modifiers.map(modifier =>
createObjectProperty(
createSimpleExpression(`data-${modifier}`, true),
createSimpleExpression('true', true),
),
)
return {
props: [
createObjectProperty(
arg || createSimpleExpression('custom', true),
exp,
),
...modifierProps,
],
needRuntime: context.helper(RESOLVE_DIRECTIVE),
}
}4.2.11 错误处理与调试
编译错误处理
typescript
// 错误代码枚举
export enum ErrorCodes {
// 转换错误
X_V_IF_NO_EXPRESSION,
X_V_IF_SAME_KEY,
X_V_ELSE_NO_ADJACENT_IF,
X_V_FOR_NO_EXPRESSION,
X_V_FOR_MALFORMED_EXPRESSION,
X_V_FOR_TEMPLATE_KEY_PLACEMENT,
X_V_BIND_NO_EXPRESSION,
X_V_ON_NO_EXPRESSION,
X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET,
X_V_SLOT_MIXED_SLOT_USAGE,
X_V_SLOT_DUPLICATE_SLOT_NAMES,
X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN,
X_V_SLOT_MISPLACED,
X_V_MODEL_NO_EXPRESSION,
X_V_MODEL_MALFORMED_EXPRESSION,
X_V_MODEL_ON_SCOPE_VARIABLE,
X_V_MODEL_ON_PROPS,
X_INVALID_EXPRESSION,
X_KEEP_ALIVE_INVALID_CHILDREN,
// 前缀标识符错误
X_PREFIX_ID_NOT_SUPPORTED,
X_MODULE_MODE_NOT_SUPPORTED,
// 通用错误
X_SCOPE_ID_NOT_SUPPORTED,
__EXTEND_POINT__,
}
// 错误消息映射
export const errorMessages: Record<ErrorCodes, string> = {
[ErrorCodes.X_V_IF_NO_EXPRESSION]: `v-if directive requires expression.`,
[ErrorCodes.X_V_IF_SAME_KEY]: `v-if/v-else-if branches must use unique keys.`,
[ErrorCodes.X_V_ELSE_NO_ADJACENT_IF]: `v-else/v-else-if has no adjacent v-if or v-else-if.`,
[ErrorCodes.X_V_FOR_NO_EXPRESSION]: `v-for directive requires expression.`,
[ErrorCodes.X_V_FOR_MALFORMED_EXPRESSION]: `v-for has invalid expression.`,
[ErrorCodes.X_V_FOR_TEMPLATE_KEY_PLACEMENT]: `<template v-for> key should be placed on the <template> tag.`,
[ErrorCodes.X_V_BIND_NO_EXPRESSION]: `v-bind directive requires expression.`,
[ErrorCodes.X_V_ON_NO_EXPRESSION]: `v-on directive requires expression.`,
// ... 更多错误消息
}
// 错误创建函数
export function createCompilerError(
code: ErrorCodes,
loc?: SourceLocation,
messages?: { [code: number]: string },
additionalMessage?: string,
): CompilerError {
const msg =
(messages || errorMessages)[code] + (additionalMessage || ``)
const error = new SyntaxError(String(msg)) as CompilerError
error.code = code
error.loc = loc
return error
}调试工具
typescript
// 转换调试器
export function createTransformDebugger(
options: {
logNodeTransforms?: boolean
logDirectiveTransforms?: boolean
logHelpers?: boolean
logHoisting?: boolean
} = {},
): TransformOptions {
const {
logNodeTransforms = false,
logDirectiveTransforms = false,
logHelpers = false,
logHoisting = false,
} = options
return {
nodeTransforms: [
// 调试节点转换器
(node, context) => {
if (logNodeTransforms) {
console.log(`[NodeTransform] Processing:`, {
type: NodeTypes[node.type],
tag: 'tag' in node ? node.tag : undefined,
loc: node.loc,
})
}
},
],
directiveTransforms: {
// 调试指令转换器包装器
debug: (dir, node, context) => {
if (logDirectiveTransforms) {
console.log(`[DirectiveTransform] Processing:`, {
name: dir.name,
arg: dir.arg,
exp: dir.exp,
modifiers: dir.modifiers,
loc: dir.loc,
})
}
return { props: [] }
},
},
// 辅助函数调试
onError: (error) => {
console.error(`[Transform Error]:`, error)
},
onWarn: (warning) => {
console.warn(`[Transform Warning]:`, warning)
},
}
}
// AST可视化工具
export function visualizeAST(node: any, depth = 0): string {
const indent = ' '.repeat(depth)
const type = NodeTypes[node.type] || 'UNKNOWN'
let result = `${indent}${type}`
if ('tag' in node && node.tag) {
result += ` <${node.tag}>`
}
if ('content' in node && node.content) {
result += ` "${node.content}"`
}
if ('children' in node && node.children) {
result += '\n'
for (const child of node.children) {
result += visualizeAST(child, depth + 1) + '\n'
}
}
return result
}4.2.12 实际应用示例
完整转换流程示例
typescript
// 示例模板
const template = `
<div class="container" :class="{ active: isActive }">
<h1>{{ title }}</h1>
<ul>
<li v-for="item in items" :key="item.id" @click="handleClick(item)">
{{ item.name }}
</li>
</ul>
<button v-if="showButton" @click="submit">Submit</button>
</div>
`
// 解析为AST
const ast = baseParse(template)
// 转换配置
const transformOptions: TransformOptions = {
hoistStatic: true,
cacheHandlers: true,
nodeTransforms: [
transformOnce,
transformIf,
transformFor,
transformElement,
transformText,
],
directiveTransforms: {
bind: transformBind,
on: transformOn,
},
}
// 执行转换
transform(ast, transformOptions)
// 转换后的AST结构(简化)
/*
{
type: NodeTypes.ROOT,
children: [
{
type: NodeTypes.ELEMENT,
tag: 'div',
codegenNode: {
type: NodeTypes.VNODE_CALL,
tag: '"div"',
props: {
type: NodeTypes.JS_OBJECT_EXPRESSION,
properties: [
{ key: 'class', value: 'container' },
{ key: 'class', value: '{ active: isActive }' }
]
},
children: [...],
patchFlag: PatchFlags.CLASS,
isBlock: true
}
}
],
helpers: Set([OPEN_BLOCK, CREATE_ELEMENT_BLOCK, ...]),
hoists: [...], // 静态提升的节点
cached: [...], // 缓存的表达式
}
*/性能优化效果
typescript
// 优化前的渲染函数(伪代码)
function render() {
return h('div', {
class: ['container', { active: isActive }]
}, [
h('h1', {}, title),
h('ul', {},
items.map(item =>
h('li', {
key: item.id,
onClick: () => handleClick(item)
}, item.name)
)
),
showButton ? h('button', { onClick: submit }, 'Submit') : null
])
}
// 优化后的渲染函数(伪代码)
function render() {
return (openBlock(), createElementBlock('div', {
class: normalizeClass(['container', { active: isActive }])
}, [
_hoisted_1, // <h1>{{ title }}</h1> 被静态提升
createElementVNode('ul', null, [
(openBlock(true), createElementBlock(Fragment, null,
renderList(items, (item) => {
return (openBlock(), createElementBlock('li', {
key: item.id,
onClick: _cache[0] || (_cache[0] = ($event) => handleClick(item))
}, toDisplayString(item.name), 1 /* TEXT */))
}), 128 /* KEYED_FRAGMENT */
))
]),
showButton
? (openBlock(), createElementBlock('button', {
key: 0,
onClick: submit
}, 'Submit'))
: createCommentVNode('v-if', true)
], 2 /* CLASS */))
}
// 静态提升的节点
const _hoisted_1 = createElementVNode('h1', null, 'Static Title')4.2.13 最佳实践与注意事项
转换器开发最佳实践
- 遵循转换顺序:确保转换器按正确顺序执行
- 处理边界情况:考虑各种异常情况和错误处理
- 性能优化:避免不必要的AST遍历和计算
- 类型安全:使用TypeScript确保类型安全
- 测试覆盖:编写全面的单元测试
常见陷阱
- 节点替换时机:在正确的阶段进行节点替换
- 上下文状态管理:正确维护转换上下文状态
- 循环引用:避免创建循环引用的AST结构
- 内存泄漏:及时清理不需要的引用
- 兼容性考虑:考虑不同版本的兼容性
调试技巧
- 使用调试器:利用浏览器调试工具
- AST可视化:使用工具可视化AST结构
- 日志记录:在关键位置添加日志
- 单元测试:编写针对性的测试用例
- 性能分析:使用性能分析工具优化
总结
Vue 3的AST转换系统是一个高度优化、模块化的编译器组件,通过插件化的转换器架构实现了:
- 高效的优化策略:静态提升、patch flag、缓存优化等
- 灵活的扩展机制:支持自定义转换器和指令
- 精确的性能控制:细粒度的更新标记和优化
- 完善的错误处理:全面的错误检测和调试支持
- 强大的类型系统:TypeScript提供的类型安全保障
这些优化使得Vue 3在运行时性能上相比Vue 2有了显著提升,同时保持了良好的开发体验和扩展性。理解转换系统的工作原理对于深入掌握Vue 3编译器、开发自定义转换器以及优化应用性能都具有重要意义。
