Vue源码学习(三)异步更新

83 阅读2分钟

Vue更新页面的方式采用的是异步的批量的更新方式,来高效的进行页面的更新

一般情况下会依照此方式进行更新,但并不代表无法尽心同步更新

watcher收集

当数据发生变化时,会触发set,然后Dep触发notify,循环执行依赖中的每个watcher的更新函数update,watcher的update方法会选择以同步或异步的方式尽心更新,默认情况下都是异步,此时会收集需要更新的watcher,在未来的某一时刻进行批量的更新,watcher会被scheduler收集到队列中,并在首次收集时调用nextTick,没错就是平时用到的nextTick,回调一个循环执行每个watcher.run的函数

nextTick

nextTick会将所有接受到的回调函数放到一个回调队列callbacks中,如果是回调队列为空(push第一个回调时),则使用Promise创建一个循环执行所有callback的微任务,此时等待浏览器事件循环,当处理微任务时,则会处理callbacks中callback,而此时如果有watcher需要更新,watcher也会执行更新任务,这也就是为什么对一个data数据改变时,立即调用dom,会发现dom中的显示并未立即改变,而需要nextTick

例子

1.

此时nextTick和Promise显示的都是3的值,因为在微任务队列中是以一下顺序存在的

[[flushCallbacks([ flushSchedulerQueue([watcher...]), $nextTick-callback ]), Promise.then]

在微任务队列中首先存在两个微任务,第一个就是nextTick的flushCallbacks,第二个则是Promise.then,而flushCallbacks中又有两个任务,分别是需要更新的watcher队列的函数和$nextTick创建的回调函数

因此第一个执行的就是watcher的更新队列,而第二个则是nextTick,因此它的值也是最新的3的值,最后才是Promise.then

2.

此时将nextTick放到了最顶部,结果nextTick中显示的确是最开始的初始值Vue,这是因为先执行了nextTick,此时还没有触发到set,因此也没有在nextTick中添加watcher更新的队列,在之后的数据变动后才添加了watcher的更新队列,因此此时的watcher更新队列处于nextTick的后边,所以是先执行的nextTick的回调函数,而此时wathcer还为执行,因此innerHTML的值还为改变,而Promise.then执行时,watcher已更新