为什么Vue.js使用异步更新队列

187 阅读2分钟

1. 为什么Vue.js使用异步更新队列

react的setData()也是这个原理吧

Vue.js中有个队列,每当需要渲染时,会将watcher推送到这个队列中,在下一次事件循环中再让watcher触发渲染流程

如果在同一轮事件循环中有两个数据发生了变化,那么组件的watcher会收到两份通知,从而进行两次渲染。事实上,并不需要渲染两次,虚拟DOM会对整个组件进行渲染,所以只需要等所有状态修改完毕后,一次性将整个组件的DOM渲染到最新即可

要解决这个问题,收到通知watcher实例添加到队列中缓存起来,并判断队列是否已经存在相同的watcher。然后在下一次事件循环中,会让队列watcher触发渲染。这样保证了即便在同一事件循环中有两个状态发生改变,watcher最后也执行一次渲染流程

2.什么是事件循环

我们都知道JavaScript是一门单线程且非阻塞的脚本语言。

微任务:

  • Promise.then
  • MutationObserver
  • Object.observe
  • process.nextTick 宏任务
  • setTimeout
  • setInterval
  • setImmediate
  • MessageChannel
  • requesAnimationFrame
  • I/O
  • UI 交互事件

3. 什么是执行栈

当我们执行了一个方法时,JavaScript会生成一个与这个方法对应的执行环境(context),又叫执行上下文。这个执行环境中有这个方法的私有作用域,上层作用域的指向,方法的参数,私有作用域中定义的变量以及this对象。这个执行环境会被添加到一个栈中,这个栈就是执行栈

new Vue({
  methods: {
    example:function(){
      // 先使用nextTick 注册回调
      this.$nextTick(function(){
        //DOM 没有更新
      })
      // 然后修改数据
      this.message = 'changed'
    }
  },
})

new Vue({
  methods: {
    example:function(){
      // 先使用setTimeout向宏任务中注册回调
    setTimeout(_ => {
      // DOM 现在更新了
    },0)
      // 然后修改数据向微任务中注册回调 ---- watcher的更新是微任务,异步更新的
      this.message = 'changed'
    }
  },
})

微任务优先级太高,也可能出现问题。也可强制使用宏任务的方法