当前位置: 首页 > news >正文

Vue 2 渲染链路剖析

“new Vue 之后发生了什么?”
“数据改变后,Vue 又做了什么?”

两个问题看似老生常谈,却足以勾勒出 Vue 2 整个运行时架构:
实例化、响应式系统、调度器、虚拟 DOM、补丁算法、组件递归、生命周期信号
下文以一次完整的 mount → render → patch → update 链路为主线,给出逐帧级别的剖析。

在这里插入图片描述

1. 实例化阶段

1.1 前期准备

构造函数内部首先执行 _init,其职责包括

  1. 规范化选项:调用 mergeOptions 把全局 mixin、extends、mixins 与实例选项融合。
  2. 建立内部属性树initLifecycle 构建 $parent / $children / $refs 等指针;initEvents 建立事件中心;initRender 绑定 createElement 别名并声明 $slots$scopedSlots
  3. 生命周期钩子 beforeCreate:此时还未注入任何用户状态,无法访问 data / computed / methods

1.2 状态注入与响应式化

initState 按顺序处理

  • initProps:校验类型,把 props 变成响应式,并代理到 vm 实例;
  • initMethods:绑定上下文;
  • initData:遍历返回对象,通过 observe 创建 Observer,递归地把每个属性转成 getter / setter
  • initComputed:为每个计算属性创建惰性的 Watcher
  • initWatch:对用户 watch 选项创建对应的 Watcher

随后触发 created。此时响应式系统已就绪,但尚未生成 DOM。

1.3 模板 → render 函数

$mount 流程根据平台区分:

  • 若存在手写 render,直接使用;
  • 否则,运行时编译器执行 compileToFunctions,把模板字符串解析为 AST,再优化、生成代码字符串,最终通过 new Function 得到 render 函数。

1.4 挂载与首帧渲染

  1. beforeMount:此时 vm.$el 已指向挂载点,但仍是原始占位节点。
  2. 创建 渲染 Watcher
    new Watcher(vm,function updateComponent() {vm._update(vm._render(), hydrating);},noop,{ isRenderWatcher: true }
    );
    
    该 Watcher 的求值函数 updateComponentrender → patch 封装成一次原子更新。
  3. 首次执行 updateComponent
    • _render() 执行 render.call(vm, …),返回 VNode Tree
    • _update(prevVNode, nextVNode) 调用 __patch__。由于 prevVNode 为空,进入 create path:递归 createElm 生成真实节点,遇到组件 VNode 则递归进入子组件的实例化流程。
  4. mounted:DOM 已插入文档,组件树整树可见。

2. 响应式系统

2.1 依赖收集

在渲染函数执行期间,任何对响应式属性的读取都会触发 Dep.depend()
当前 渲染 Watcher 被压入 Dep.subs 数组,完成“谁依赖我”的登记。

2.2 变更通知

响应式属性被写入时,setter → Dep.notify() 遍历 subs,调用每个 Watcher 的 update()
渲染 Watcher 的 update 把自身放入 异步队列

queueWatcher(this);

nextTick(flushSchedulerQueue) 把队列清空,确保同一轮事件循环内的多次数据变更只触发一次重渲染。

3. 重新渲染:diff 与补丁

3.1 调度阶段

  1. beforeUpdate:此时 DOM 仍是旧状态,适合读取布局或手动保存滚动位置。
  2. 调度器执行渲染 Watcher 的 run(),再次调用 updateComponent

3.2 重新求值

  • _render() 生成新的 VNode 树;
  • 旧的依赖被 清除resetDep()),新的依赖被再次收集,实现“按需追踪”。

3.3 虚拟 DOM diff

_update(prevVNode, nextVNode) 进入 patch

  • 同层比较:O(n) 时间复杂度,基于双端对比的优化策略;
  • 节点类型不一致:直接替换,旧节点走销毁链路;
  • 节点类型一致
    • 普通元素 → patchVnode,比对属性、子节点;
    • 组件 → 调用组件自身的 updateComponent,递归进入本流程;
    • 文本 → 直接替换 textContent

3.4 销毁链路

当 diff 算法发现组件需要被移除时,执行

oldComponentInstance.$destroy();

其内部顺序为

  1. beforeDestroy:实例仍完全可用;
  2. 递归销毁子组件;
  3. 解绑所有指令、事件、Watcher;
  4. destroyed:实例与其 DOM 解耦,等待 GC。

3.5 更新后钩子

diff 与补丁完成后触发 updated,此时 DOM 已与新状态保持同步。

4. 全链路鸟瞰

new Vue(options)├─ beforeCreate├─ initState  (props, data, computed, watch)├─ created├─ beforeMount├─ render()  → VNode├─ patch(null, VNode)  → 真实 DOM└─ mounted响应式属性变更├─ setter → Dep.notify()├─ Watcher.update() → queueWatcher├─ nextTick → flushSchedulerQueue├─ beforeUpdate├─ render() → new VNode├─ patch(oldVNode, newVNode)└─ updated

5. 工程启示

  • created 内进行网络请求可避免首屏阻塞;
  • 任何导致布局抖动的读取应在 beforeUpdate 完成;
  • 避免在 updated 中直接修改响应式数据,否则可能触发级联更新;
  • 组件级缓存(keep-alive)通过复用实例,绕过完整的 mount → destroy 链路,显著提升性能。

结语

掌握 new Vue 之后的完整调用链,等价于把 Vue 2 运行时拆解成五个子系统:
选项合并、响应式追踪、异步调度器、虚拟 DOM 运行时、生命周期信号
这五个子系统协同完成“数据驱动视图”的承诺,也构成了我们在面试桌上、调试器中、性能分析工具里反复看到的那张全景图。

http://www.dtcms.com/a/316603.html

相关文章:

  • Linux逻辑卷管理操作指南
  • Arxiv-Daily
  • AUTOSAR进阶图解==>AUTOSAR_RS_ECUResourceTemplate
  • 【前端】使用jQuery播放图片,类似播放幻灯片一样
  • Redis面试精讲 Day 11:Redis主从复制原理与实践
  • RAG向量检索增强生成
  • MediaPipe框架解析(五):c++ face_mesh解析
  • TDengine 中 TDgpt 的模型评估工具
  • 基于WOA鲸鱼优化的VMD-GRU时间序列预测算法matlab仿真
  • 代码随想录day57图论7
  • (ZipList入门笔记一)ZipList的节点介绍
  • 【RH124 问答题】第 6 章 管理本地用户和组
  • ⭐CVPR2025 MatAnyone:稳定且精细的视频抠图新框架
  • LLM开发——语言模型会根据你的提问方式来改变答案
  • Android与Flutter混合开发:页面跳转与通信完整指南
  • 深入剖析 RAG 检索系统中的召回方式:BM25、向量召回、混合策略全解析
  • Go语言 string
  • stm32项目(21)——基于STM32和MPU6050的体感机械臂开发
  • 跨尺度目标漏检率↓82.4%!陌讯多尺度融合算法在占道经营识别的实战优化
  • 结构化开发方法详解:软件工程的奠基性范式
  • 机器学习——贝叶斯
  • Android 之 Kotlin中的协程(Dispatchers.IO)
  • Android UI 组件系列(十一):RecyclerView 多类型布局与数据刷新实战
  • ara::log::LogStream::WithTag的概念和使用案例
  • 鸿蒙开发--web组件
  • Java技术栈/面试题合集(5)-SpringBoot篇
  • SpringBoot3.x入门到精通系列:4.1 整合 MongoDB 详解
  • 《四种姿势用Java玩转AI大模型:从原生HTTP到LangChain4j》
  • Ubuntu24.04环境下非DOCKER方式安装Mysql5.7
  • 今日行情明日机会——20250805