异步渲染

Vue可以响应数据变化,数据变化后会自动更新视图,如果每次修改都触发视图更新,会导致多次重复和不必要的渲染操作,例如一个组件使用了两个data的属性,更新两个属性如果触发两次渲染的话,会影响性能。因此Vue采取异步更新。

每次更新响应的属性之后,会将渲染的watcher放到一个队列中,在下个事件循环中再执行。

整个更新渲染的过程是:

  1. data的属性更新
  2. 通知依赖的render
  3. render的watcher加入队列(队列去重)
  4. nextTick清空watcher队列,执行各watcher中的回调

示例:

  1. // 改变数据
  2. vm.message = 'changed';
  3. // 想要立即使用更新后的DOM。这样不行,因为设置message后DOM还没有更新
  4. console.log(vm.$el.textContent); // 并不会得到'changed'
  5. // nextTick里面的代码会在DOM更新后执行
  6. Vue.nextTick(function(){
  7. console.log(vm.$el.textContent) //可以得到'changed'
  8. });

nextTick

在了解nextTick之前,需要先理解JavaScript的任务队列。

Vue.nextTick()方法用来设置让方法在下一个事件队列中执行。

Vue使用MutationObserver/Promise/setTimeout实现nextTick。Vue判断浏览器兼容性,按照MutationObserver -> Promise -> setTimeout的优先级实现nextTick。

Promise和setTimeout比较好理解,使用MutationObserver的目的是为了将回调加入到微任务队列,比宏任务队列更早执行。

我们知道,NodeJS中也有一个process.nextTick方法,也是用来设置回调异步,在下个循环中执行,但是这个方法并未将任务加入到宏任务队列或者微任务队列,它是将任务加到下次事件循环之前。详细的说明参考文章【5】。

参考文章

深入解读VUE中的异步渲染的实现【1】

为何Vue采用异步渲染【2】

Vue.nextTick 的原理和用途【3】

VueJS源码学习——MutationObserver实现nextTick【4】

JavaScript 运行机制详解:再谈Event Loop【5】