返回首页

Vue底层理解--渲染器、编译器

布莱克2026-03-17 15:22已编辑
Tip:文章封面与内容无关,作者旅游时拍摄,因为没什么值得把四季都错过!

渲染器:

将虚拟DOM变为真实DOM并渲染到浏览器页面中

主要由三个部分组成:

  1. 创建阶段 (createRenderer):定义底层的平台操作。
  2. 渲染阶段 (render):接收一个 VNode,并将其挂载(Mount)或更新(Patch)到容器上。
  3. 打补丁阶段 (patch):渲染器的“大脑”,负责决定是创建新节点、移动旧节点、还是仅仅更新属性

当你在代码中修改了响应式数据,渲染器的流水线便开始运转:

第一步:开启调度 (Schedule)

数据变动触发组件的 effect 重新执行。为了性能,这个执行会被丢进调度器(Scheduler)的微任务队列中。

第二步:生成 VNode 树 (Render)

当微任务开始刷新,执行组件的 render 函数。此时编译器(Compiler)生成的代码会通过 h 函数或 createVNode 生成一棵全新的虚拟 DOM 树。

第三步:深度递归 Patch

这是渲染器的核心逻辑。渲染器会拿着“新旧两棵树”进行对比。

  • 处理组件 (Process Component):如果 VNode 类型是组件,渲染器会递归地为该子组件开启渲染流程。
  • 处理元素 (Process Element):如果 VNode 是 HTML 标签:挂载 (Mount):如果是第一次渲染,渲染器会根据 VNode 创建真实 DOM 节点,并递归处理其子节点,最后插入容器。更新 (Patch):对比新旧节点的 props(样式、事件等)和 children(子节点)。

编译器:

编译器的作用是将模板编译为渲染函数

在传统的模板编译(如 Vue 2)中,即使一个 HTML 节点是完全静态的(内容永远不变),它也会被放在 render 函数内部。这意味着每次组件更新,这些静态节点都要被重新创建一遍 VNode。

核心原理

Vue 3 编译器会识别出那些不带任何变量、指令或绑定属性的节点,将其提升到渲染函数之外

  • 减少重复创建:这些静态 VNode 只会在模块首次加载时创建一次,之后的所有渲染都直接复用同一个对象。
  • 内存优化:避免了频繁的垃圾回收(GC)压力,因为没有大量临时 VNode 对象产生。
// 编译前
<div>
  <span class="label">姓名:</span>
  <span>{{ name }}</span>
</div>

// 编译后(简化版)
const _hoisted_1 = _createVNode("span", { class: "label" }, "姓名:", -1 /* HOISTED */)

export function render(_ctx) {
  return (_openBlock(), _createBlock("div", null, [
    _hoisted_1, // 直接引用外部变量
    _createVNode("span", null, _toDisplayString(_ctx.name), 1 /* TEXT */)
  ]))
}

A. 解析阶段 (Parse)

将模板字符串解析为 抽象语法树 (AST)

B. 转换阶段 (Transform)

这是编译器最核心的部分。它会递归遍历 AST,并应用一系列 插件(Plugins)

  1. 静态分析:标记哪些节点是动态的,哪些是静态的。
  2. 补丁标记 (Patch Flags):给动态节点打上“便条”(如:这个节点只有 class 会变)。
  3. 提升逻辑:执行我们上面提到的静态提升。
  4. Block 追踪:将动态节点收集到父级的 dynamicChildren 数组中,形成 Block Tree。

C. 生成阶段 (Code Generation)

将优化后的 AST 转化为最终的渲染函数字符串。在这个阶段,它会根据转换阶段留下的线索,生成最精简的代码

assistant