目录前言一、「渲染调度机制」总览二、为什么需要“调度”三、Vue 的队列1、Vue 组件更新会立即执行吗2、Vue 的三种核心队列1、调度总入口queueJob()2、queueFlush()开启“异步更新”3、真正执行更新flushJobs()①、先执行 pre 队列②、再执行组件更新render patch③、最后执行 post 队列3、为什么还要排序四、调度机制解决了哪些问题五、nextTick 本质是什么⭐️⭐️⭐️⭐️⭐️六、整个调度流程串起来前言Vue3 在响应式数据触发更新时不会立即执行组件渲染而是通过调度器将更新任务放入队列并利用微任务在当前事件循环结束后统一批量执行。调度器内部会对任务去重并按照组件创建顺序排序确保父子组件更新顺序正确。同时 Vue 还维护 pre 和 post 两类副作用队列用于控制 watch 等副作用的执行时机从而实现高性能、可预测的 UI 更新机制。一、「渲染调度机制」总览Vue3 渲染调度机制 把多次状态变化合并起来按顺序、分批次、异步地执行组件更新关键词就三个队列去重异步批处理二、为什么需要“调度”看一段代码state.countstate.countstate.count如果每次修改都立刻trigger → effect → render → patch那页面会 连续重渲染 3 次纯纯浪费性能。Vue 的目标是不管你一口气改多少次数据一个组件一轮事件循环只更新一次这就是调度器的使命。三、Vue 的队列1、Vue 组件更新会立即执行吗不会。Vue 组件更新不会立刻执行而是先进“更新队列”。当响应式数据触发 trigger() 时triggerEffects(dep)内部不会直接执行组件的副作用而是queueJob(effect)也就是说❗组件更新 被放进一个“待执行任务队列”2、Vue 的三种核心队列队列存什么什么时候执行queue组件更新任务主要渲染阶段pendingPreFlushCbswatchEffect/watch(flush:‘pre’)渲染前pendingPostFlushCbswatch(..., { flush:post })DOM 更新后1、调度总入口queueJob()源码逻辑简化constqueue:Job[][]letisFlushingfalseexportfunctionqueueJob(job){if(!queue.includes(job)){// ⭐ 去重queue.push(job)queueFlush()}}关键点 1去重同一个组件不管触发多少次只会保留一个更新任务。2、queueFlush()开启“异步更新”functionqueueFlush(){if(!isFlushing){isFlushingtruePromise.resolve().then(flushJobs)}}关键点 2微任务异步执行Vue 使用的是Promise.then → 微任务这就解释了为什么state.countconsole.log(dom)// 旧 DOMawaitnextTick()console.log(dom)// 新 DOM因为 DOM 更新在 本轮同步代码执行完后才发生。3、真正执行更新flushJobs()functionflushJobs(){try{flushPreFlushCbs()// 1️⃣ 执行 pre 队列queue.sort(sortJob)// 2️⃣ 排序父 → 子for(jobofqueue){// 3️⃣ 执行组件更新job()}}finally{flushPostFlushCbs()// 4️⃣ 执行 post 队列resetSchedulerState()}}执行顺序非常重要先执行 pre 队列再执行组件更新render patch最后执行 post 队列①、先执行 pre 队列用于watchEffect(fn)// 默认 flush: prewatch(source,fn)此时 DOM 还没更新适合做读取旧 DOM 状态计算下一步逻辑②、再执行组件更新render patch就是我们熟悉的effect → render → patch这一步才真正改 DOM。③、最后执行 post 队列用于watch(source,fn,{flush:post})此时 DOM 已经是最新的。适合访问更新后的 DOM操作第三方库3、为什么还要排序queue.sort((a,b)getId(a)-getId(b))组件创建时有递增 id父组件 id子组件 id排序的意义是✅ 保证父组件先更新子组件后更新否则可能出现子组件基于旧 props 更新父组件晚更新导致数据错乱四、调度机制解决了哪些问题问题Vue 怎么解决多次数据变更频繁重渲染队列去重 批量执行更新顺序错乱按组件创建顺序排序DOM 访问时机混乱pre / post 队列分离同步更新阻塞 UI微任务异步更新五、nextTick 本质是什么⭐️⭐️⭐️⭐️⭐️exportfunctionnextTick(fn?){returnfn?Promise.resolve().then(fn):Promise.resolve()}它只是等待当前这轮“组件更新批处理”结束。所以state.countawaitnextTick()// 这里 DOM 一定是新的六、整个调度流程串起来响应式数据改变 │ ▼trigger()│ ▼queueJob(组件effect)│ ▼Promise.then(微任务)│ ▼flushJobs()│ │ ▼ ▼ pre队列 组件renderpatch │ ▼ post队列