您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息
免费发信息
三六零分类信息网 > 红河分类信息网,免费分类信息发布

Vue3组件异步更新和nextTick运行机制源码分析

2024/2/26 11:26:49发布20次查看
组件的异步更新我们应该都知道或者听说过组件的更新是异步的,对于nexttick我们也知道它是利用promise将传入的回调函数放入微任务队列中,在函数更新完以后执行,那么既然都是异步更新,nexttick是怎么保证回调会在组件更新后执行,其插入队列的时机又是什么时候?带着这些问题我们去源码中寻找答案。
先回顾一下组件更新的effect:
const effect = (instance.effect = new reactiveeffect( componentupdatefn, () => queuejob(update), // updata: () => effect.run() 返回值 componentupdatefn // 将effect添加到组件的scope.effects中 instance.scope // track it in component's effect scope))
在响应式数据发生改变触发effect执行的时候会执行() => queuejob(update)调度器,所以我们要去看queuejob干了什么
queuejob// packages/runtime-core/src/scheduler.tsexport function queuejob(job: schedulerjob) { if ( !queue.length || !queue.includes( // queue中是否已经存在相同job job, isflushing && job.allowrecurse ? flushindex + 1 : flushindex ) ) { if (job.id == null) { // 向queue添加job queue是一个数组 queue.push(job) } else { queue.splice(findinsertionindex(job.id), 0, job) } // 执行queueflush queueflush() }}
queuejob主要是将scheduler添加到queue队列,然后执行queueflush
queueflushfunction queueflush() { // isflushing和isflushpending初始值都是false // 说明当前没有flush任务在执行,也没有flush任务在等待执行 if (!isflushing && !isflushpending) { // 初次执行queueflush将isflushpending设置为true 表示有flush任务在等待执行 isflushpending = true // resolvedpromise是promise.resolve() // flushjobs被放到微任务队列中 等待所有同步scheduler执行完毕后执行 // 这样就可以保证flushjobs在一次组件更新中只执行一次 // 更新currentflushpromise 以供nexttick使用 currentflushpromise = resolvedpromise.then(flushjobs) }}
flushjobs当所有的同步scheduler执行完毕后,就会去处理微任务队列的任务,就会执行flushjobs回调函数
function flushjobs(seen?: countmap) { // 状态改为有flush正在执行 isflushpending = false isflushing = true if (__dev__) { seen = seen || new map() } // sort queue before flush. // this ensures that: // 1. components are updated from parent to child. (because parent is always // created before the child so its render effect will have smaller // priority number) // 2. if a component is unmounted during a parent component's update, // its update can be skipped. // 组件更新的顺序是从父到子 因为父组件总是在子组件之前创建 所以它的渲染效果将具有更小的优先级 // 如果一个组件在父组件更新期间被卸载 则可以跳过它的更新 queue.sort(comparator) ... // 先执行queue中的job 然后执行pendingpostflushcbs中的job // 这里可以实现watch中的 postflush try { for (flushindex = 0; flushindex < queue.length; flushindex++) { const job = queue[flushindex] if (job && job.active !== false) { if (__dev__ && check(job)) { continue } // console.log(`running:`, job.id) // 执行job callwitherrorhandling(job, null, errorcodes.scheduler) } } } finally { // job执行完毕后清空队列 flushindex = 0 queue.length = 0 // 执行flushpostflushcbs 此时组件已经更新完毕 flushpostflushcbs(seen) isflushing = false currentflushpromise = null // some postflushcb queued jobs! // keep flushing until it drains. if (queue.length || pendingpostflushcbs.length) { flushjobs(seen) } }}
总结:组件内当修改响应式数据后,组件更新函数会被放到queue中,然后注册一个微任务,这个微任务负责执行queue中的所有job,所以这时就算我们同步修改多次/多个响应式数据,同一个组件的更新函数只会被放入一次到queue中,等到同步操作结束后才会去执行注册的微任务,组件更新函数才会被执行,组件才会被更新。
nexttickvue3中nexttick的实现非常简单:
export function nexttick<t = void>( this: t, fn?: (this: t) => void): promise<void> { const p = currentflushpromise || resolvedpromise // nexttick回调函数放到currentflushpromise的微任务队列中 return fn ? p.then(this ? fn.bind(this) : fn) : p}
这里的关键就是currentflushpromise,如果你足够细心就会发现currentflushpromise其实在queueflush中就被赋值了,它正是把执行组件更新函数的任务放入微队列中的promise,所以在此我们拿到currentflushpromise正好把nexttick接收到的函数回调放到微队列中flushjobs的后面,等到flushjobs执行完成后组件也已经更新完毕,此时正是我们希望去执行nexttick回调的时机!
以上就是vue3组件异步更新和nexttick运行机制源码分析的详细内容。
红河分类信息网,免费分类信息发布

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录