基于 vuejs/core 编译器核心
2026-03-16 | 技术深度解读
第一部分:基础架构
第二部分:优化策略
第三部分:核心类解析
第四部分:核心函数
Vue 3 编译器 是将模板转换为渲染函数的核心引擎,实现了多项编译时优化。
核心职责
关键文件
compile.ts - 编译入口transform.ts - AST 转换codegen.ts - 代码生成cacheStatic.ts - 静态优化compiler-core/
├── src/
│ ├── compile.ts // 主编译入口
│ ├── parse.ts // 模板解析器
│ ├── ast.ts // AST 节点定义
│ ├── transform.ts // AST 转换核心
│ ├── codegen.ts // 代码生成器
│ └── transforms/ // 转换插件目录
│ ├── vIf.ts // v-if 转换
│ ├── vFor.ts // v-for 转换
│ ├── vOn.ts // v-on 转换
│ ├── vBind.ts // v-bind 转换
│ ├── vModel.ts // v-model 转换
│ ├── cacheStatic.ts // 静态提升
│ └── transformElement.ts // 元素转换
Template String
↓
[Parser] → 模板解析
↓
AST (抽象语法树)
↓
[Transform] → AST 转换 + 优化标记
↓
Transformed AST (带优化信息)
↓
[Codegen] → 代码生成
↓
Render Function Code
三阶段流水线:Parse → Transform → Generate
| 特性 | Vue 2 | Vue 3 |
|---|---|---|
| 编译产物 | 完整的渲染函数 | 优化的渲染函数 |
| 静态处理 | 每次重新创建 | 静态提升 |
| 更新标记 | 全量 diff | Patch Flags |
| 动态内容 | 递归比对 | Block 树 |
| 事件处理 | 每次创建新函数 | 缓存优化 |
1. 静态提升
2. Patch Flags
3. Block 树
4. 缓存机制
// compile.ts - 编译入口
export function baseCompile(
source: string | RootNode,
options: CompilerOptions = {},
): CodegenResult {
// 1. 解析配置
const prefixIdentifiers =
!__BROWSER__ && (options.prefixIdentifiers === true || isModuleMode)
// 2. 解析模板生成 AST
const ast = isString(source)
? baseParse(source, resolvedOptions)
: source
// 3. 获取转换插件
const [nodeTransforms, directiveTransforms] =
getBaseTransformPreset(prefixIdentifiers)
// 4. 执行 AST 转换
transform(ast, extend({}, resolvedOptions, {
nodeTransforms: [...nodeTransforms, ...options.nodeTransforms],
directiveTransforms: extend({}, directiveTransforms, options.directiveTransforms),
}))
// 5. 生成代码
return generate(ast, resolvedOptions)
}
// 获取基础转换插件集
export function getBaseTransformPreset(
prefixIdentifiers?: boolean,
): TransformPreset {
return [
[ // NodeTransforms - 节点转换插件
transformVBindShorthand, // v-bind 简写
transformOnce, // v-once
transformIf, // v-if
transformMemo, // v-memo
transformFor, // v-for
trackVForSlotScopes, // v-for 作用域追踪
transformExpression, // 表达式转换
transformSlotOutlet, // slot 插槽
transformElement, // 元素转换 ⭐核心
trackSlotScopes, // 插槽作用域
transformText, // 文本合并
],
{ // DirectiveTransforms - 指令转换
on: transformOn, // v-on
bind: transformBind, // v-bind
model: transformModel // v-model
},
]
}
export interface TransformContext {
// 编译选项
root: RootNode
prefixIdentifiers: boolean
hoistStatic: boolean
cacheHandlers: boolean
// 状态管理
helpers: Map<symbol, number> // 运行时助手
components: Set<string> // 组件引用
directives: Set<string> // 指令引用
hoists: JSChildNode[] // 提升的节点
cached: CacheExpression[] // 缓存的表达式
// 作用域追踪
scopes: {
vFor: number
vSlot: number
vPre: number
vOnce: number
}
// 当前处理位置
parent: ParentNode | null
currentNode: RootNode | TemplateChildNode | null
// 核心方法
helper(name: symbol): symbol
hoist(exp: JSChildNode): SimpleExpressionNode
cache(exp: JSChildNode): CacheExpression
}
const context: TransformContext = {
// 注册运行时助手
helper(name) {
const count = context.helpers.get(name) || 0
context.helpers.set(name, count + 1)
return name
},
// 静态提升:将表达式提升到 render 函数外
hoist(exp) {
context.hoists.push(exp)
return createSimpleExpression(
`_hoisted_${context.hoists.length}`,
false, exp.loc, ConstantTypes.CAN_CACHE
)
},
// 缓存表达式(用于 v-once 等)
cache(exp, isVNode = false) {
const cacheExp = createCacheExpression(
context.cached.length, exp, isVNode
)
context.cached.push(cacheExp)
return cacheExp
},
// 替换当前节点
replaceNode(node) {
context.parent.children[context.childIndex] =
context.currentNode = node
}
}
// 深度优先遍历 AST
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) {
exitFns.push(onExit) // 收集退出回调
}
if (!context.currentNode) return // 节点被移除
node = context.currentNode // 节点可能被替换
}
// 2. 递归遍历子节点
switch (node.type) {
case NodeTypes.IF:
node.branches.forEach(b => traverseNode(b, context))
break
case NodeTypes.FOR:
case NodeTypes.ELEMENT:
case NodeTypes.ROOT:
traverseChildren(node, context)
break
}
// 3. 执行退出回调(后序处理)
exitFns.forEach(fn => fn())
}
// 遍历子节点
export function traverseChildren(
parent: ParentNode,
context: TransformContext,
): void {
let i = 0
// 节点移除时的回调
const nodeRemoved = () => { i-- }
for (; i < parent.children.length; i++) {
const child = parent.children[i]
if (isString(child)) continue // 跳过纯文本
// 更新上下文位置
context.grandParent = context.parent
context.parent = parent
context.childIndex = i
context.onNodeRemoved = nodeRemoved
// 递归处理子节点
traverseNode(child, context)
}
}
关键点:转换器可以在遍历时修改节点,context 自动追踪当前位置
export interface RootNode extends Node {
type: NodeTypes.ROOT
source: string // 原始模板
children: TemplateChildNode[] // 子节点
// 编译时收集的信息
helpers: Set<symbol> // 需要的运行时助手
components: string[] // 使用的组件
directives: string[] // 使用的指令
// 优化相关
hoists: JSChildNode[] // 提升的静态节点
cached: CacheExpression[] // 缓存的表达式
// 代码生成
codegenNode?: TemplateChildNode // 根节点代码生成入口
transformed?: boolean // 是否已转换
temps: number // 临时变量数量
}
helpers 包含如 CREATE_VNODE, TO_DISPLAY_STRING 等运行时函数引用
// 元素类型枚举
export enum ElementTypes {
ELEMENT, // 普通元素 <div>
COMPONENT, // 组件 <MyComp>
SLOT, // 插槽 <slot>
TEMPLATE, // 模板 <template>
}
// 基础元素节点
export interface BaseElementNode extends Node {
type: NodeTypes.ELEMENT
tag: string
tagType: ElementTypes
props: Array<AttributeNode | DirectiveNode>
children: TemplateChildNode[]
codegenNode?: VNodeCall // 生成的 VNode 调用
}
// 普通元素节点
export interface PlainElementNode extends BaseElementNode {
tagType: ElementTypes.ELEMENT
codegenNode: VNodeCall | SimpleExpressionNode
}
// VNode 调用表达式(代码生成核心)
export interface VNodeCall extends Node {
type: NodeTypes.VNODE_CALL
tag: string | symbol | CallExpression // 标签名
props: PropsExpression | undefined // 属性对象
children: ChildrenType | undefined // 子节点
// 优化标记 ⭐
patchFlag: PatchFlags | undefined // 更新标记
dynamicProps: string | undefined // 动态属性名
directives: DirectiveArguments | undefined
// Block 相关
isBlock: boolean // 是否为 Block
disableTracking: boolean // 是否禁用追踪
isComponent: boolean // 是否为组件
}
VNodeCall 是转换阶段生成的中间表示,最终会转换为 createVNode() 调用
// 静态常量类型(用于优化判定)
export enum ConstantTypes {
NOT_CONSTANT = 0, // 非常量(有动态内容)
CAN_SKIP_PATCH, // 可以跳过 patch
CAN_CACHE, // 可以缓存
CAN_STRINGIFY, // 可以字符串化(最静态)
}
// 判定逻辑:
// CAN_STRINGIFY > CAN_CACHE > CAN_SKIP_PATCH > NOT_CONSTANT
| 类型 | 含义 | 优化策略 |
|---|---|---|
| CAN_STRINGIFY | 纯静态内容 | 提升 + 字符串化 |
| CAN_CACHE | 可缓存内容 | 提升或缓存 |
| CAN_SKIP_PATCH | 无需 patch | 跳过 diff |
| NOT_CONSTANT | 动态内容 | 正常处理 |
export enum NodeTypes {
// 模板节点
ROOT, // 根节点
ELEMENT, // 元素
TEXT, // 文本
COMMENT, // 注释
INTERPOLATION, // 插值 {{ msg }}
ATTRIBUTE, // 属性
DIRECTIVE, // 指令
// 表达式节点
SIMPLE_EXPRESSION, // 简单表达式
COMPOUND_EXPRESSION, // 复合表达式
// 结构节点
IF, // v-if
IF_BRANCH, // v-if 分支
FOR, // v-for
TEXT_CALL, // 文本调用
// 代码生成节点
VNODE_CALL, // VNode 调用
JS_CALL_EXPRESSION, // JS 函数调用
JS_OBJECT_EXPRESSION, // JS 对象
JS_ARRAY_EXPRESSION, // JS 数组
JS_FUNCTION_EXPRESSION,// JS 函数
JS_CONDITIONAL_EXPRESSION, // 三元表达式
JS_CACHE_EXPRESSION, // 缓存表达式
}
// 静态提升和缓存的核心函数
export function cacheStatic(
root: RootNode,
context: TransformContext
): void {
walk(root, undefined, context, !!getSingleElementRoot(root))
}
function walk(
node: ParentNode,
parent: ParentNode | undefined,
context: TransformContext,
doNotHoistNode: boolean = 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.CAN_CACHE) {
// 标记为缓存节点
child.codegenNode.patchFlag = PatchFlags.CACHED
toCache.push(child)
continue
}
}
// ... 递归处理子节点
}
}
export function getConstantType(
node: TemplateChildNode,
context: TransformContext,
): ConstantTypes {
const { constantCache } = context
switch (node.type) {
case NodeTypes.ELEMENT:
// 检查缓存的判定结果
const cached = constantCache.get(node)
if (cached !== undefined) return cached
// 1. Block 节点通常不是常量
if (codegenNode.isBlock &&
node.tag !== 'svg' &&
node.tag !== 'foreignObject') {
return ConstantTypes.NOT_CONSTANT
}
// 2. 检查属性
const propsType = getGeneratedPropsConstantType(node, context)
if (propsType === ConstantTypes.NOT_CONSTANT)
return ConstantTypes.NOT_CONSTANT
// 3. 递归检查子节点
for (const child of node.children) {
const childType = getConstantType(child, context)
if (childType === ConstantTypes.NOT_CONSTANT)
return ConstantTypes.NOT_CONSTANT
}
return ConstantTypes.CAN_STRINGIFY
case NodeTypes.TEXT:
case NodeTypes.COMMENT:
return ConstantTypes.CAN_STRINGIFY // 纯文本总是静态
case NodeTypes.INTERPOLATION:
return getConstantType(node.content, context)
}
}
✅ 可以提升
<div>static</div>❌ 不能提升
{{ msg }}:class="cls"v-if, v-for提升效果:静态节点只创建一次,多次渲染复用同一引用
export const transformElement: NodeTransform = (node, context) => {
// 在退出时执行(子节点已处理)
return function postTransformElement() {
if (node.type !== NodeTypes.ELEMENT) return
const { tag, props } = node
const isComponent = node.tagType === ElementTypes.COMPONENT
// 1. 解析组件类型
let vnodeTag = isComponent
? resolveComponentType(node, context)
: `"${tag}"`
// 2. 处理属性 → 生成 props 对象
const propsResult = buildProps(node, context)
const { props: vnodeProps, patchFlag, dynamicPropNames } = propsResult
// 3. 处理子节点
const vnodeChildren = isComponent
? buildSlots(node, context)
: processChildren(node)
// 4. 创建 VNodeCall 节点
node.codegenNode = createVNodeCall(
context, vnodeTag, vnodeProps, vnodeChildren,
patchFlag, // 优化标记
dynamicPropNames, // 动态属性
undefined, // directives
shouldUseBlock, // 是否为 Block
false, // disableTracking
isComponent
)
}
}
export function buildProps(
node: ElementNode,
context: TransformContext,
): {
props: PropsExpression | undefined
patchFlag: number
dynamicPropNames: string[]
} {
let patchFlag = 0
const dynamicPropNames: string[] = []
for (const prop of props) {
if (prop.type === NodeTypes.ATTRIBUTE) {
// 静态属性
properties.push(createObjectProperty(prop.name, prop.value))
} else {
// 指令处理
const directiveTransform = context.directiveTransforms[prop.name]
if (directiveTransform) {
const { props: dirProps, needRuntime } = directiveTransform(prop, node)
properties.push(...dirProps)
// PatchFlag 分析
dirProps.forEach(analyzePatchFlag)
}
}
}
// 收集 PatchFlags
if (hasClassBinding) patchFlag |= PatchFlags.CLASS
if (hasStyleBinding) patchFlag |= PatchFlags.STYLE
if (dynamicPropNames.length) patchFlag |= PatchFlags.PROPS
return { props: propsExpression, patchFlag, dynamicPropNames }
}
const analyzePatchFlag = ({ key, value }: Property) => {
if (isStaticExp(key)) {
const name = key.content
// 跳过缓存的事件处理器
if (value.type === NodeTypes.JS_CACHE_EXPRESSION) return
// 跳过常量值
if (getConstantType(value, context) > 0) return
// 标记不同的动态类型
if (name === 'ref') {
hasRef = true
} else if (name === 'class') {
hasClassBinding = true
} else if (name === 'style') {
hasStyleBinding = true
} else if (name !== 'key') {
dynamicPropNames.push(name)
}
} else {
hasDynamicKeys = true // 动态 key 需要 FULL_PROPS
}
}
// 最终标记
if (hasDynamicKeys) patchFlag |= PatchFlags.FULL_PROPS
else {
if (hasClassBinding) patchFlag |= PatchFlags.CLASS
if (hasStyleBinding) patchFlag |= PatchFlags.STYLE
if (dynamicPropNames.length) patchFlag |= PatchFlags.PROPS
}
export function generate(
ast: RootNode,
options: CodegenOptions = {},
): CodegenResult {
const context = createCodegenContext(ast, options)
const { push, indent, deindent, newline } = context
// 1. 生成前置代码(import 语句)
if (mode === 'module') {
genModulePreamble(ast, context)
} else {
genFunctionPreamble(ast, context)
}
// 2. 生成提升的静态节点
genHoists(ast.hoists, context)
// 3. 生成 render 函数
push(`function render(_ctx, _cache) {`)
indent()
// 4. 生成返回的 VNode 树
push(`return `)
genNode(ast.codegenNode, context)
deindent()
push(`}`)
return { code: context.code, ast }
}
function genVNodeCall(node: VNodeCall, context: CodegenContext) {
const { tag, props, children, patchFlag, dynamicProps, isBlock } = node
// 生成 patchFlag 注释(开发模式)
let patchFlagString = patchFlag
if (__DEV__ && patchFlag) {
const flagNames = Object.keys(PatchFlagNames)
.filter(n => patchFlag & n)
.map(n => PatchFlagNames[n])
patchFlagString = `${patchFlag} /* ${flagNames.join(', ')} */`
}
// Block 包装
if (isBlock) {
push(`(${helper(OPEN_BLOCK)}(), `)
}
// VNode 创建调用
const helper = isBlock
? getVNodeBlockHelper(inSSR, isComponent)
: getVNodeHelper(inSSR, isComponent)
push(`${helper(helper)}(`)
genNodeList([tag, props, children, patchFlagString, dynamicProps])
push(`)`)
if (isBlock) push(`)`)
}
function genHoists(
hoists: JSChildNode[],
context: CodegenContext
) {
if (!hoists.length) return
context.pure = true // 添加 PURE 注释
const { push, newline } = context
newline()
// 为每个提升的节点生成常量声明
for (let i = 0; i < hoists.length; i++) {
const exp = hoists[i]
if (exp) {
push(`const _hoisted_${i + 1} = `)
genNode(exp, context) // 递归生成节点代码
newline()
}
}
context.pure = false
}
// 生成的代码示例:
// const _hoisted_1 = /*#__PURE__*/ createElementVNode("div", null, "Static", -1)
// const _hoisted_2 = /*#__PURE__*/ createElementVNode("span", null, "Text", -1)
function genCacheExpression(
node: CacheExpression,
context: CodegenContext
) {
const { push, helper, indent, deindent, newline } = context
const { needPauseTracking, needArraySpread } = node
// 数组展开(避免内存泄漏)
if (needArraySpread) {
push(`[...(`)
}
// 缓存读取或初始化
push(`_cache[${node.index}] || (`)
if (needPauseTracking) {
indent()
push(`${helper(SET_BLOCK_TRACKING)}(-1),`)
newline()
push(`(`)
}
// 缓存赋值
push(`_cache[${node.index}] = `)
genNode(node.value, context)
if (needPauseTracking) {
push(`).cacheIndex = ${node.index},`)
newline()
push(`${helper(SET_BLOCK_TRACKING)}(1),`)
newline()
push(`_cache[${node.index}]`)
deindent()
}
push(`)`)
if (needArraySpread) push(`)]`)
}
模板
<div>
<span class="static">
Static Text
</span>
<p>{{ dynamic }}</p>
</div>
编译产物
// 静态节点提升到函数外
const _hoisted_1 = /*#__PURE__*/
createElementVNode("span",
{ class: "static" },
"Static Text"
)
function render(_ctx, _cache) {
return createElementVNode("div", null, [
_hoisted_1, // 复用
createElementVNode("p", null,
toDisplayString(_ctx.dynamic)
)
])
}
// PatchFlags 定义(位运算标记)
export const enum PatchFlags {
TEXT = 1, // 动态文本内容
CLASS = 2, // 动态 class
STYLE = 4, // 动态 style
PROPS = 8, // 动态属性(非 class/style)
FULL_PROPS = 16, // 有动态 key 的属性
HYDRATE_EVENTS = 32,// 事件监听器
STABLE_FRAGMENT = 64, // 稳定的 fragment
KEYED_FRAGMENT = 128, // 有 key 的 fragment
UNKEYED_FRAGMENT = 256,// 无 key 的 fragment
NEED_PATCH = 512, // 需要 patch(ref 等)
DYNAMIC_SLOTS = 1024, // 动态插槽
DEV_ROOT_FRAGMENT = 2048, // 开发模式
CACHED = -1, // 缓存标记(v-once)
Bail = -2 // 退出优化
}
优势:运行时通过位运算快速判断更新类型,跳过不必要的 diff
什么是 Block?
// Block 节点
const block = {
type: Fragment,
dynamicChildren: [
vnode1, // 动态子节点
vnode2
],
patchFlag: PatchFlags.PROPS
}
Block 边界
每个 Block 维护 dynamicChildren 数组,直接跳过静态内容
// 事件处理器缓存
<button @click="handleClick">
// 编译为:
return createElementVNode("button", {
onClick: _cache[0] || (
_cache[0] = (...args) => (
_ctx.handleClick && _ctx.handleClick(...args)
)
)
})
// v-once 缓存
<div v-once>{{ msg }}</div>
// 编译为:
_cache[1] || (
setBlockTracking(-1),
_cache[1] = createElementVNode("div", null,
toDisplayString(_ctx.msg), 1 /* TEXT */
),
setBlockTracking(1),
_cache[1]
)
// transformOnce.ts
export const transformOnce: NodeTransform = (node, context) => {
if (findDir(node, 'once')) {
context.inVOnce = true
return () => {
context.inVOnce = false
// 为节点生成缓存表达式
const codegenNode = node.codegenNode
if (codegenNode) {
node.codegenNode = context.cache(
codegenNode,
true, // isVNode
true // inVOnce
)
}
}
}
}
// 缓存效果:
// - 只渲染一次
// - 后续渲染直接复用
// - 跳过整个子树的 diff
// v-memo: 条件缓存
<div v-memo="[value]">
{{ expensive() }}
</div>
// 编译为:
withMemo(
[value], // 依赖数组
() => createElementVNode("div", null,
toDisplayString(expensive())
),
_cache, 0
)
// transformMemo.ts
export const transformMemo: NodeTransform = (node, context) => {
if (findDir(node, 'memo')) {
return () => {
const memoExp = dir.exp
node.codegenNode = createCallExpression(
WITH_MEMO,
[
memoExp, // 依赖数组
memoFactory, // 工厂函数
`_cache`,
String(context.cached++)
]
)
}
}
}
| 优化 | 编译时 | 运行时 |
|---|---|---|
| 静态提升 | 识别静态节点 | 直接复用 |
| Patch Flags | 标记动态类型 | 按标记更新 |
| Block 树 | 确定 Block 边界 | 收集动态子节点 |
| 事件缓存 | 生成缓存代码 | 复用处理器 |
| v-once | 生成缓存表达式 | 跳过更新 |
核心理念:将尽可能多的工作在编译时完成,减少运行时开销
// NodeTransform 就是一个访问者
export type NodeTransform = (
node: RootNode | TemplateChildNode,
context: TransformContext,
) => void | (() => void) | (() => void)[]
// 使用示例
const myTransform: NodeTransform = (node, context) => {
// 进入节点时的处理
if (node.type === NodeTypes.ELEMENT) {
console.log('Entering:', node.tag)
}
// 返回退出回调
return () => {
// 退出节点时的处理(子节点已处理)
console.log('Exiting:', node.tag)
}
}
// 注册转换器
context.nodeTransforms.push(myTransform)
优点:解耦遍历逻辑和节点处理,支持插件化扩展
// 两种类型的转换器
// 1. NodeTransform - 节点转换
export type NodeTransform = (
node: RootNode | TemplateChildNode,
context: TransformContext,
) => void | (() => void)
// 2. DirectiveTransform - 指令转换
export type DirectiveTransform = (
dir: DirectiveNode,
node: ElementNode,
context: TransformContext,
) => {
props: Property[] // 生成的属性
needRuntime?: boolean // 是否需要运行时指令
}
// 插件组合示例
const preset = getBaseTransformPreset()
preset[0].push(customNodeTransform) // 添加节点转换
preset[1].custom = customDirectiveTransform // 添加指令转换
transform(ast, {
nodeTransforms: preset[0],
directiveTransforms: preset[1]
})
┌─────────────┐
│ Template │
│ String │
└──────┬──────┘
│ baseParse()
▼
┌─────────────┐
│ RootNode │──┬─ children
│ (AST) │ ├─ helpers
└──────┬──────┘ └─ components
│ transform()
▼
┌─────────────────────┐
│ Transformed AST │──┬─ codegenNode
│ │ ├─ hoists[]
└──────┬──────────────┘ └─ cached[]
│ generate()
▼
┌─────────────────────┐
│ Render Function │──┬─ imports
│ Code │ ├─ hoisted vars
└─────────────────────┘ └─ render()
transform(ast, { hoistStatic: true })
│
▼
traverseNode()
│
▼
┌──────────────────┐
│ cacheStatic() │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ getConstantType()│◀───────┐
└────────┬─────────┘ │
│ │
┌────────▼─────────┐ │
│ CAN_STRINGIFY? │ │
└────────┬─────────┘ │
Yes │ No │
│ └─────────────┘
▼ (递归检查)
┌──────────────────┐
│ context.hoist() │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ 生成 _hoisted_N │
└──────────────────┘
generate(ast)
│
├─ genModulePreamble() // import 语句
│
├─ genHoists() // 静态提升变量
│ │
│ └─ const _hoisted_1 = ...
│
├─ push('function render() {')
│
├─ genNode(codegenNode) // 生成 VNode 树
│ │
│ ├─ genVNodeCall()
│ │ │
│ │ └─ createVNode(...)
│ │
│ ├─ genCallExpression()
│ │
│ └─ genCacheExpression()
│
└─ push('}')
│
▼
{ code: string, ast, map }
| 指标 | Vue 2 | Vue 3 | 提升 |
|---|---|---|---|
| 初始渲染 | 100% | 55% | ~2x |
| 更新性能 | 100% | ~20% | ~5x |
| 内存占用 | 100% | ~50% | ~2x |
| 包体积 | 23KB | 10KB | Tree-shakable |
关键优化:Patch Flags + Block 树 + 静态提升 = 极致性能
Vue 2
function render() {
return _c('div', [
_c('span', {
staticClass: 'static'
}, [_v("Static")]),
_c('p', [_v(_s(msg))])
])
}
// 每次渲染都创建所有 VNode
Vue 3
const _hoisted_1 =
createElementVNode("span",
{ class: "static" },
"Static"
)
function render(_ctx, _cache) {
return createElementVNode("div", null, [
_hoisted_1, // 复用
createElementVNode("p", null,
toDisplayString(_ctx.msg),
1 /* TEXT */
)
])
}
// 静态节点只创建一次
静态内容占比
| 静态程度 | 提升效果 |
|---|---|
| 90% 静态 | ~90% 减少创建 |
| 50% 静态 | ~50% 减少创建 |
| 10% 静态 | ~10% 减少创建 |
更新性能
| 动态类型 | Patch Flag | 优化 |
|---|---|---|
| 仅文本 | TEXT (1) | 直接更新 |
| 仅 class | CLASS (2) | 跳过其他 |
| 仅 style | STYLE (4) | 跳过其他 |
利用静态提升
<!-- ✅ 好 -->
<div>
<span>Static</span>
{{ dynamic }}
</div>
<!-- ❌ 避免 -->
<div :class="cls">
<span :id="id">Text</span>
</div>
合理使用 v-once
<!-- ✅ 适合 v-once -->
<div v-once>
<h1>Static Header</h1>
</div>
<!-- ❌ 不适合 -->
<div v-once>
{{ dynamicContent }}
</div>
1. 过度使用 v-once
<!-- ❌ 反模式:动态内容用 v-once 不会更新 -->
<div v-once>{{ counter }}</div>
2. 不必要的响应式
// ❌ 反模式
const staticData = ref({ type: 'static' })
// ✅ 正确
const staticData = { type: 'static' }
3. 复杂表达式
<!-- ❌ 难以优化 -->
<div :class="isA ? 'a' : isB ? 'b' : 'c'">
查看编译产物
// 使用 Vue Template Explorer
// https://vue-next-template-explorer.netlify.app/
// 或在代码中
import { compile } from 'vue'
const { code } = compile(template, {
hoistStatic: true,
cacheHandlers: true
})
console.log(code)
检查 Patch Flags
// 开发模式下查看
// VNode 上的 patchFlag 属性
import { PatchFlags } from 'vue'
// 检查标记
if (vnode.patchFlag & PatchFlags.TEXT) {
console.log('Has dynamic text')
}
| 框架 | 编译策略 | 优化方式 |
|---|---|---|
| Vue 3 | AOT 编译 | Patch Flags + Block + Hoist |
| React | JSX 编译 | Fiber + Reconciliation |
| Svelte | AOT 到原生 JS | 无运行时 + 细粒度更新 |
| Angular | AOT 编译 | Ivy + 增量 DOM |
| Solid | JSX 编译 | 细粒度响应式 + 无 VDOM |
编译器改进
新特性
Vapor Mode:实验性的无虚拟 DOM 模式,性能接近原生 DOM 操作
核心优化
关键文件
compile.ts - 入口transform.ts - 转换cacheStatic.ts - 静态优化codegen.ts - 代码生成核心思想:编译时尽可能多地完成工作,运行时只做必要的更新
推荐工具:Vue Template Explorer - 实时查看编译产物
基于源码分析
2026-03-16 | 查克小助理