当前位置: 首页 > Web前端 > vue.js

Vue3nextTick调度分析

时间:2023-03-31 22:29:01 vue.js

nextTick定义:推迟到下一个DOM更新周期之后回调,改变一些数据后立即使用,等待DOM更新。在实践中,这种方法一般用于组件更新。您需要获取更新的数据,因此使用nextTick等待DOM更新=>{message.value=newMessage//这里的值为旧值awaitnextTick()//nextTick后,获取DOM的更新值console.log('NowDOMisupdated')}}})这个api使用起来相当简单,也很容易理解,但是要想知道它是什么意思,还是需要看源码来了解它的执行机制导出函数nextTick(this:ComponentPublicInstance|void,fn?:()=>void):Promise{constp=currentFlushPromise||已解决的Promisereturnfn?p.then(this?fn.bind(this):fn):p}以上是vue源码中nextTick的实现。为了理解实现逻辑,需要理解变量currentFlushPromise的含义,所以从任务调度机制入手分析任务调度首先,这个调度机制的作用在runtime-core/的调度器文件API中/这个文件会抛出以下API函数nextTick(){}//任务队列清空后执行该函数queueJob(){}//添加一个任务并开始执行任务队列invalidateJob(){}//删除taskqueuePreFlushCb(){}//添加前置回调函数,开始执行任务队列queuePostFlushCb(){}//添加后回调函数,开始执行任务队列flushPreFlushCbs(){}//执行前回调函数flushPostFlushCbs(){}//执行后回调函数我们首先要知道几个关键变量letisFlushing=false//是否正在运行清除任务队列letisFlushPending=false//清除任务已经创建,等待清除constqueue:SchedulerJob[]=[]//任务队列letflushIndex=0//索引任务队列中当前正在执行的任务,然后我们从queueJob函数开始/*这个函数主要是入队一个任务,如果满足条件则启动queueFlush*/exportfunctionqueueJob(job:SchedulerJob){/***任务可入队逻辑*1.任务队列为空*2.任务队列中不能存在待入队的任务(根据情况分析)*/if((!queue.length||!queue.includes(job,/*当队列正在被清空且当前处于待处理状态时,团队任务可以重复执行ive,表示当前任务必须和当前正在执行的任务是同一个任务,所以+1是保证待入队的任务和正在执行的任务相同,但和待入队的任务不一样稍后执行*/isFlushing&&job.allowRecurse?flushIndex+1:flushIndex))&&job!==currentPreFlushParentJob){//二分查找任务在队列中的位置constpos=findInsertionIndex(job)if(pos>-1){queue.splice(pos,0,作业)}else{queue.push(作业)}queueFlush()}}queueFlushfunctionqueueFlush(){/**创建清空任务后,禁止再次创建更多清空任务,因为队列操作完成后,flushJobs会递归清空所有任务队列,所以只需要一个清空任务*/if(!isFlushing&&!isFlushPending){isFlushPending=true/*团队清理任务创建成功,记录当前团队清理任务。这个标记可以用来为nextTick创建一个自定义函数,表示nextTick的执行时间是在清队任务之后,其实从这个地方就可以理解nextTick的执行原理*/currentFlushPromise=resolvedPromise.then(flushJobs)}}flushJobs//清空任务队列functionflushJobs(seen?:CountMap){isFlushPending=false//关闭flushingQueue任务等待状态isFlushing=true//开启队列状态isbeingflushedif(__DEV__){seen=见过||newMap()}//清空回调前任务队列flushPreFlushCbs(seen)/*任务队列中的任务按照ID排序原因1.因为组件更新是从父组件到子组件,而任务update是在数据源更新的时候触发的,所以为了更新任务的顺序,需要排序2.如果父组件组件更新的时候已经卸载了,那么子组件的更新任务可以被跳过*/queue.sort((a,b)=>getId(a)-getId(b))try{//遍历任务队列for(flushIndex=0;flushIndex执行主任务队列->执行回调后任务队列