codegen 阶段将优化后的 ast 转为 render 函数字符串,核心是 generate() 入口,通过 genelement 递归生成节点代码;vue 2 用 with(this) 和 \_c/\_v/\_s,vue 3 改用显式参数、语义化函数及静态提升。

codegen 阶段是 Vue 编译流程的最终落点,它把优化后的 AST 转换成一段可执行的 JavaScript 函数字符串——也就是 render 函数。这个函数在组件实例化时被调用,返回虚拟 DOM(vnode),驱动后续的挂载与更新。整个过程不是魔法,而是结构清晰、逐节点映射的代码生成逻辑。
核心入口:generate() 函数
所有工作始于 generate(ast, options),它接收已优化的 AST 树和编译选项,返回一个包含 render 字符串和 staticRenderFns 数组的对象。
- 内部会创建一个
CodegenState实例,封装平台相关配置(如元素创建函数名、辅助方法别名等) - 真正干活的是
genElement(ast, state),它递归遍历 AST 节点,按类型分发处理 - 最终拼出类似
"_c('div', {class:xxx}, [_c('span', [], [_v(_s(msg))])])"的字符串,再包裹成new Function(`with(this){return ${code}}`)(Vue 2)或直接返回箭头函数体(Vue 3)
节点类型决定生成策略
每个 AST 节点的 type 和指令属性,直接决定调用哪个生成器:
-
普通元素节点(type === 1):检查是否含
v-if、v-for、v-once等指令;有则调用对应生成器(如genIf()、genFor()),否则走默认路径——拼出__h__('tag', data, children)或_createElementBlock()调用 -
插值表达式(type === 2):转为
_toDisplayString($setup.msg)(Vue 3)或_s(msg)(Vue 2),确保响应式值被正确读取与转换 -
纯文本(type === 3):直接输出字符串字面量,如
"hello";若被标记为静态,则可能被提升到staticRenderFns中复用 - template 标签或 slot:跳过自身,递归生成其子节点内容,不产生独立 DOM 元素
Vue 2 与 Vue 3 的关键差异
虽然目标一致,但实现细节因架构演进而不同:
-
作用域处理:Vue 2 依赖
with(this)注入上下文,易引发严格模式报错;Vue 3 彻底弃用,改用显式参数_ctx和_cache,更安全、更利于 Tree-shaking -
辅助函数命名:Vue 2 使用
_c(create)、_v(vnode 文本)、_s(stringify);Vue 3 改为语义化别名,如_createElementVNode→_createElementBlock,并引入_openBlock管理动态块 -
静态提升:Vue 3 在 codegen 阶段主动将静态 vnode 提取为常量(如
const _hoisted_1 = [' ']),避免每次 render 重复创建,性能更优
指令解析如何嵌入 render 字符串
v-if、v-for 等不是简单“翻译”,而是生成带控制流的 JS 表达式:
-
v-if="show"→ 生成三元表达式:show ? [branchA] : [branchB],分支内仍是__h__或_createElementBlock调用 -
v-for="(item, i) in list"→ 生成_l(list, (item, i) => __h__(...))(Vue 2)或_renderList(list, (item, i) => _createElementBlock(...))(Vue 3),其中_l是运行时循环辅助函数 -
:class="{active: isActive}"→ 在data参数中生成对象字面量:{ class: { active: isActive } },交由运行时合并类名










