Vue渲染性能分析应聚焦响应式追踪、虚拟DOM diff、更新调度三大链路:源码分别位于reactivity/src/、runtime-core/src/renderer.ts与diff.ts、scheduler.ts;结合DevTools可精准定位trigger频繁、key失配致全量重建、queueJob堆积等瓶颈。

要通过源码分析 Vue 的渲染性能,关键不是通读全部源码,而是聚焦在响应式追踪、虚拟 DOM 生成与 diff、组件更新调度这三个核心链路。Vue 3 的源码结构清晰(基于 TypeScript + monorepo),可精准定位关键模块,结合 DevTools 和 Performance 面板交叉验证,快速识别瓶颈所在。
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
从源码角度看 Vue 渲染流程的三个关键环节
响应式依赖收集与触发更新
源码位置:packages/reactivity/src/(effect.ts、reactive.ts、track.ts、trigger.ts)
核心逻辑:组件首次渲染时调用render(),访问响应式数据触发getter→track()将当前effect(即组件的update函数)加入该属性的依赖集合;数据变更时调用setter→trigger()遍历依赖并标记为“待更新”。
性能线索:若某个深层对象被频繁读取(如state.user.profile.address.city),会为每一层都建立依赖,导致trigger时遍历开销陡增。可通过shallowRef或markRaw避免非必要代理。虚拟 DOM 生成与 patch 过程
源码位置:packages/runtime-core/src/renderer.ts(patch函数)、packages/runtime-core/src/vnode.ts(createVNode)、packages/runtime-core/src/diff.ts(patchKeyedChildren等)
关键观察点:patch函数判断节点类型后分发处理;列表 diff 使用双端比较算法,但前提是key唯一且稳定。若v-for中key用index,源码中patchKeyedChildren会误判移动/复用关系,导致大量moveNode和重复createVNode。
可验证:在patchElement或processElement中加断点,看是否频繁执行hostPatchProp(DOM 属性更新)或hostInsert(节点插入)。组件更新队列与 nextTick 调度
源码位置:packages/runtime-core/src/scheduler.ts(queueJob、flushJobs)、packages/shared/src/index.ts(nextTick实现)
Vue 不是数据一变就立刻重渲染,而是将所有effect推入queue,在 microtask 末尾统一flushJobs。若队列中 job 数量异常多(比如上千个组件同时响应一个全局状态),flushJobs循环耗时会显著上升。
源码提示:queue.length > 100时已属高风险;flushJobs内部无节流,全量同步执行 —— 这正是“卡顿”在主线程的根源。
如何动手做一次有效源码级分析
- 在本地项目中启用
app.config.performance = true,打开 Vue DevTools 的 Performance 面板录制操作 - 回放时点击高耗时色块,查看调用栈中是否深入到
reactive.ts的trigger、renderer.ts的patch或scheduler.ts的flushJobs - 对应在
node_modules/vue中打开对应文件(或直接在 VS Code 中Go to Definition跳转),在关键函数入口加debugger或console.time - 重点关注:
-
trigger被调用次数是否远超预期(如一个count++触发了 200+ 个 effect 更新) -
patch中isSameVNodeType返回 false 的频率(说明节点类型不一致,无法复用) -
queueJob后queue.length是否持续堆积(可能因异步操作未清理、watch 无限循环等)
-
一个典型问题的源码印证路径
现象:滚动长列表时 CPU 占用飙升,帧率掉到 15fps
→ DevTools Performance 显示大量 componentUpdate 耗时集中在 patch
→ 查 patch 调用栈,发现频繁进入 patchKeyedChildren
→ 检查模板:<div v-for="(item, i) in list" :key="i">
→ 源码中 patchKeyedChildren 对比 prevChild.key 和 nextChild.key,当 list 发生 unshift 或 filter,i 全体偏移 → 所有 key 失配 → 全量销毁重建
→ 改为 :key="item.id" 后,patchKeyedChildren 快速定位复用节点,patch 耗时下降 80%
不复杂但容易忽略


















