当前位置: 首页 > Web前端 > JavaScript

vue0.11版本源码阅读系列之五:如何批量更新_0

时间:2023-03-27 01:58:35 JavaScript

第三篇vue0.11版本源码阅读系列之三:在指令编译中,我们知道如果某个属性的值发生变化,会调用依赖属性的watcher的更新方法:p.update=function(){if(!config.async||config.debug){this.run()}else{batcher.push(this)}它不直接调用指令方法的更新,而是调用batcher。本文将介绍批处理程序的作用。顾名思义,batcher是batch的意思,所以是批量更新,为什么要批量更新,先看下面的情况:我出来了

Metoo
window.vm.show=truewindow.vm.show=false比如两条指令依赖同一个属性或者连续修改某个属性,如果批处理异步update不执行,那么多次修改dom显然没有必要。看下面两个动图,可以更直观的感受:不进行批量异步更新时:进行批量异步更新:可以明显发现,通过异步更新可以跳过中间不必要的渲染,优化性能。接下来我们看一下具体的实现。第一个是push函数://定义了两个队列,一个用来存放用户的watcher,一个用来存放指令更新的watchervarqueue=[]varuserQueue=[]varhas={}varwaiting=falsevarflushing=falseexports.push=function(job){//job是一个watcher实例varid=job.id//has[id]用于跳过重复添加同一个watcherif没有flushing(!id||!has[id]||flushing){has[id]=1//首先要说明的是$watch方法或者watch选项生成的watcher代表的是用户,并且user属性为true//这里的注释说用户的watcher在任务执行过程中可能会触发非用户命令的更新,所以触发的命令应该立即更新,否则变量flushing是不必要的if(flushing&&!job.user){job.run()return}//根据指令类型加入不同队列;(job.user?userQueue:queue).push(job)//直到清空之前的队列才会创建新队列if(!waiting){waiting=true_.nextTick(flush)}}}Whatthepushmethod做的就是把watcher添加到queue队列中,然后如果没有flush抛给nextTick或者上次抛给nextTick的flush方法已经执行完了,那就试试吧。flush方法用于遍历队列中的watchers,调用其run方法。run方法最终会调用指令的update方法来更新页面。functionflush(){flushing=truerun(queue)run(userQueue)//清空队列并重置变量reset()}functionrun(queue){//循环执行watcher实例的run方法,watcher在run方法中会遍历实例的指令队列并执行指令的update方法for(vari=0;i{xxxx})也会将回调添加到这个数组中。还有一个变量pending,用来控制重复添加的问题,最后添加到事件循环的队列中最重要的是,handle方法在批处理中很容易理解。它们都放在一个队列中,最后一起执行就是批量执行。但是要理解为什么可以异步调用MutationObserver的回调或者setTimeout的回调,需要先了解JavaScript语言中的事件循环。事件循环的原理。简单的说,因为JavaScript是单线程的,任务需要排队执行,只有前一个执行完才能执行下一个。不过有些任务比较耗时,不需要等待,可以先搁置一下,先执行后面的。等到可以执行了再执行,比如一些IO操作,比如常见的鼠标键盘事件注册,Ajax请求,settimeout定时器,Promise回调等。所以会有两个队列,一个是同步队列,一个是主线程,一个是异步队列。如果刚才提到的事件的回调能够执行,就会放到异步队列中。当主线程上的任务执行完成后,会去取异步队列中的任务执行,所以同步代码总是先于异步代码执行,执行完后会去检查异步队列,从而实现连续loop是事件循环。但是异步任务其实有两种,一种叫宏任务,常见的有:setTimeout、setInterval,还有一种叫微任务,常见的有:Promise、MutationObserver。微任务将在宏任务之前执行,即使宏任务回调先排队也是如此。下面我们来分析一下异步更新的原理。就拿开头提到的例子来说:I'mout
IAlso
window.vm.show=truewindow.vm.show=false因为有两条指令依赖show,表达方式不同,所以会有两个watcher,两个watcher都是show属性Collection的dep,所以每次修改show的值都会触发这两个watcher的更新,即会调用两次batcher.push(this)方法,第一次调用后会执行_.nextTick(flush)注册回调,不断修改show的值twice会调用上面提到的batcher.push(this)方法四次。因为过滤掉了重复添加,所以最后会在队列中添加两个watcher。以上操作是Synchronous任务,所以是连续执行的。这些同步任务执行完后,会接管刚才注册的回调句柄执行,也就是会同时执行刚才添加的两个watcher:以上是vue异步更新的全部内容。