Codegen(代码生成阶段)
编译管线最后阶段,将优化后的 AST 转换为可执行的目标代码(JavaScript)。各框架 Codegen 输出的代码结构差异巨大——Svelte 输出命令式 DOM 操作,React Compiler 输出带缓存包裹的原始组件,Vue 输出带 Patch Flags 的渲染函数,Qwik 输出多个懒加载 chunk 文件。
核心概念
Codegen(Code Generation)是编译管线三阶段(Parse → Transform → Codegen)的最后环节。Transform 阶段在 AST 上注入了优化标记和结构重写,Codegen 遍历这棵优化后的 AST,输出最终的 JavaScript 代码。
Codegen 的输出直接决定了三件事:运行时的执行模型(有无 VDOM diff)、调试体验(编译产物和源码的差距)、SSR 水合成本(编译产物是否含有 DOM 精确路径)。
各框架的 Codegen 输出特征
Svelte 5
输出命令式 DOM 操作代码——模板被转成 template() 静态克隆 + $.child() 精确 DOM 引用 + $.template_effect() signal→DOM 直接绑定。源码和编译产物差距最大,Source Map 精度最低。
Vue 3
输出带 Patch Flags 的渲染函数——createElementVNode("div", { class: cls }, "text", 2 /* CLASS */) 中的数字参数是 Transform 阶段注入的优化标记。静态提升的节点被提到渲染函数外部作为模块级常量。<script setup> 的逻辑代码几乎不变,调试体验好。
React Compiler
输出带缓存包裹的原始组件代码——_c(n) 分配缓存槽位,$[0] !== dep 做浅比较,命中缓存则跳过 JSX 创建。组件函数的整体结构保留,但多了编译器注入的缓存基础设施代码($ 数组和条件检查)。
Solid
输出模板克隆 + insert() 运行时订阅建立——和 Svelte 结构相似(都是命令式 DOM 操作),但关键差异在于 Solid 的 insert() 内部通过 createRenderEffect 在运行时动态建立依赖关系,而非编译期静态确定。
Qwik
唯一输出多个文件的编译器。主 bundle 中事件处理函数被替换为字符串引用("chunk-xxx.js#handler_0"),实际逻辑被拆到独立的懒加载 chunk 文件中。每个 chunk 包含 useLexicalScope 的闭包恢复代码。
Codegen 输出与后续链路的因果关系
| 框架 | 与源码差距 | 输出文件数 | 调试体验 | 水合收益 |
|---|---|---|---|---|
| Svelte 5 | 极大 | 1 | 差(Source Map 精度低) | 高(DOM 路径精确) |
| Vue 3 | 中(模板大/逻辑小) | 1 | 好(逻辑部分不变) | 中(Patch Flags 加速比对) |
| React Compiler | 小(多了缓存包裹) | 1 | 中偏下(缓存代码不可见) | 零(首次必 miss) |
| Solid | 大 | 1 | 中(JSX 结构保留但被 insert 包裹) | 高(DOM 路径精确) |
| Qwik | 极大 | 多个 chunk | 最差(跨文件调试) | 最高(接近零水合) |
引用本术语的文章