vue 的工作流程
如果你已经握着 Vue 3 最核心的引擎——响应式系统。
现在,我们要把这个“引擎”装进车里,看看它是如何配合编译器(Compiler)和渲染器(Runtime/Renderer),让整个 Vue 项目跑起来的。
Vue 的工作流程可以被概括为一个闭环:编译 -> 挂载 -> 响应式更新 -> 补丁 (Diff)。
我们用一张逻辑图来把它们串起来:
第一阶段:编译时 (Compile Time) —— “把模版变成代码”
我们在 .vue 文件里写的 <template>,浏览器是看不懂的。需要编译器把它翻译成 JS 代码。
-
Parse (解析):把
<div id="app">{{ msg }}</div>解析成 AST (抽象语法树)。 -
Transform (转换):这是 Vue 3 优化的重灾区。它会分析 AST,标记出哪些是静态节点(永远不变),哪些是动态节点(绑定了响应式数据)。
- PatchFlags:给动态节点打上标记(比如:这个节点只有
TEXT变了,或者只有CLASS变了)。
- PatchFlags:给动态节点打上标记(比如:这个节点只有
-
Generate (生成):把优化后的 AST 生成为 渲染函数 (Render Function)。
产出物: 一个 render 函数。
// 编译后的 render 函数长这样(简化版)function render(ctx) { return h('div', { id: 'app' }, ctx.msg)}第二阶段:运行时初始化 (Mount) —— “引擎点火”
这里是响应式系统和渲染器第一次握手的地方。
-
初始化数据:组件里的
data()被调用,通过reactive()或ref()变成响应式对象(就是你写的Proxy)。 -
建立副作用 (Render Effect):
-
Vue 会创建一个组件级别的副作用函数(类似于你写的
watchEffect)。它的工作是执行render函数。 -
首次执行 Render:
- 副作用函数开始执行。
- 调用
render()函数生成 虚拟 DOM (Virtual DOM / VNode)。 - 关键点来了: 在执行
render时,读取了ctx.msg(响应式数据)。 - 触发 track: 响应式系统把这个“组件副作用”收集到了
msg的依赖桶里。
-
挂载 (Patch):渲染器根据生成的 VNode,创建真实的 DOM 节点,插入页面。
第三阶段:响应式更新 (Update) —— “数据驱动视图”
现在用户修改了数据:this.msg = 'Hello Vue'。
- **触发 **set:拦截器工作。
- 触发 trigger:响应式系统找到
msg对应的依赖——也就是组件的渲染副作用 (Render Effect)。 - 调度执行:组件更新逻辑被推入微任务队列(
queueJob,就是你学的调度器)。
第四阶段:Diff 算法与 Patch —— “精准手术”
副作用函数重新执行,这次不是初始化,而是更新。
-
重新 Render:再次调用
render(),生成一棵新的 VNode 树。 -
Diff (对比):
- 渲染器拿着 新 VNode 和 旧 VNode 进行对比。
- 利用编译时的优化:还记得第一阶段的 PatchFlags 吗?如果标记显示只有文本变了,Diff 算法就只对比文本,完全跳过属性对比和子节点对比。
- 最长递增子序列:如果涉及到列表(
v-for)的乱序移动,Vue 3 会用这个算法计算出最小移动次数。
-
Patch (打补丁):根据 Diff 的结果,只更新变动的那一小块真实 DOM。
特别篇:数据双向绑定 (v-model)
v-model,它其实不是什么魔法,而是上面流程的一个语法糖组合拳。
<input v-model="text" />
在编译时,它会被展开成两个东西:
- 数据绑定:
:value="text" - 事件监听:
@input="text = $event.target.value"
工作流串联:
- 用户输入:触发 input 事件。
- 事件回调:执行
text = ...(修改响应式数据)。 - 响应式触发:
text的 setter 触发 trigger。 - 组件更新:Render Effect 重新跑,生成新 VNode。
- DOM 更新:Input 的 value 属性被更新(虽然用户已经输进去了,但 Vue 会确保数据流的一致性)。
总结:全景图
试着把你自己写的代码代入到这个流程里:
- 编译器把
.vue变成render函数。 - 运行时创建一个
effect来运行render。 render读取数据,你的 **Proxy **get 被触发,把effect存入Set。- 数据变化,你的 **Proxy **set 被触发,调用
trigger。 trigger通知effect重新运行。effect重新运行render得到新 VNode。- 渲染器 对比新旧 VNode,调用
DOM API更新页面。
现在你已经明白了它们是怎么串联的。
- 响应式:你已经通关了。
- 编译时:这是一个纯文本处理工程,非常枯燥但强大。
- 运行时 (Renderer/Diff):这是把虚拟变成现实的关键。