Transform(编译变换阶段)
编译器对 AST 进行遍历和修改的阶段,是前端框架编译优化的核心战场。各框架的编译差异主要体现在 Transform 阶段——Vue 注入 Patch Flags 和静态提升标记,Svelte 生成响应式绑定代码,React Compiler 划分缓存区域,Qwik 提取闭包做代码拆分。
核心概念
Transform 是编译管线三阶段(Parse → Transform → Codegen)的中间环节。Parse 产出 AST 后,Transform 对 AST 做一系列变换 pass(遍历 + 修改),注入优化标记或重写节点结构,为最终的 Codegen(代码生成)提供优化后的中间表示。
Transform 是"编译器的三重身份"(消灭 diff / 加速 diff / 推迟加载)的核心分界线——同一段源码经过不同框架的 Transform 后,AST 上携带的优化信息完全不同,决定了 Codegen 输出什么样的目标代码。
各框架的 Transform 策略
Svelte 5
- 模板静态分析:识别哪些模板绑定是静态的(编译期可确定依赖)、哪些需要运行时追踪
- 响应式绑定生成:
$state标记的变量被转换为 signal 原语调用($.source),模板中的{count}被转换为$.template_effect(() => set_text(text, $.get(count)))的精确 DOM 更新 - 事件委托转换:
onclick被转换为element.__click的委托式处理
Vue 3
- Patch Flags 注入:在 VNode 创建调用中添加位标记参数(如
2 /* CLASS */),告诉运行时 diff 只需检查标记的属性 - 静态提升(Static Hoisting):将不含动态绑定的 VNode 提升到渲染函数外部,标记为
HOISTED = -1 - Block Tree 优化:用
openBlock+createBlock创建扁平化的动态节点数组,将 diff 复杂度从 O(模板大小) 降到 O(动态节点数)
React Compiler
- HIR(高级中间表示)构建:将组件函数的控制流转换为 SSA 形式
- 响应式值推导:分析哪些值是"响应式的"(来自 props / state / context),哪些是纯计算可缓存的
- 缓存区域划分:将组件函数体切分为多个作用域,每个作用域对应一个缓存槽位(
$[n]),只有当作用域的依赖值变化时才重新计算
Qwik Optimizer
$函数体提取:将$后缀标记的函数体从当前模块中提取出来,生成独立的懒加载 chunk 引用- 闭包变量分析:遍历被提取函数体的 AST,收集所有捕获的外部变量,为
useLexicalScope的序列化/反序列化生成胶水代码 - 事件代理映射:将
onClick$转换为 HTML 属性字符串引用(on:click="chunk-xxx.js#handler_0")
Transform 是编译优化的核心战场
Parse 阶段各框架差异不大(都是把文本变成树),Codegen 阶段是机械地把优化后的 AST 转成目标代码——真正决定编译器"做了什么优化"的,是 Transform 阶段注入了什么信息、重写了什么结构。
| 框架 | Transform 核心工作 | 优化目标 |
|---|---|---|
| Svelte 5 | 模板绑定→signal 直连 DOM | 消灭运行时 diff |
| Vue 3 | 注入 Patch Flags + 静态提升 | 加速运行时 diff |
| React Compiler | 缓存区域划分 + 依赖推导 | 减少不必要的重渲染 |
| Qwik | 函数体提取 + 闭包序列化分析 | 推迟 JS 加载时机 |
| Solid | JSX→insert/cloneNode 转换 | 消灭 diff(但依赖收集在运行时) |
引用本术语的文章