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)
Solid1中(JSX 结构保留但被 insert 包裹)高(DOM 路径精确)
Qwik极大多个 chunk最差(跨文件调试)最高(接近零水合)

引用本术语的文章