1.Vue如何进行异步更新呢?
将 每个 watcher 放入 queue 全局队列中` => `调用 nextTick() 方法将刷新 watcher 队列的方法 flushSchedulerQueue 放入 callbacks 数组中` => `将刷新 callbacks 数组的函数 flushCallbacks 通过 timerFunc() 方法放进浏览器的异步任务队列中` => `最后浏览器遍历执行 callbacks 数组中的刷新 watcher 队列方法 flushSchedulerQueue` => `刷新 watcher 队列方法遍历执行 queue 队列的每个 watcher.before() 和 watcher.run() 方法` => `继续下一次异步更新
以下是 update() 方法详情:
-
首先判断两个特殊标记
- 是否为 lazy 懒更新,则设置 dirty 为 true,以标记当前 watcher 为懒更新
- 再判断是否有 sync 同步更新标记,直接执行
watcher.run(),Vue 官方不推荐使用,文档没有该属性。
-
然后将 watcher 放入 queue 队列中,放入队列有两种方式,以 flushing 标志判断
- 若无在刷新队列中,直接 push 进 queue 队列
- 若正在刷新队列中,按 watcher.id 进行升序排序,确保更新的顺序
-
然后调用 nextTick(),将 flushSchedulerQueue(刷新当前 watcher 队列的方法)放入 callbacks 数组中。若浏览器的任务队列中无 flushCallbacks 函数,则执行 timerFunc()。(用 pending 来判断控制)
-
timerFunc() 将 flushCallbacks 函数(执行第 3 点中 callbacks 数组中的所有 flushSchedulerQueue 方法)放入浏览器的异步任务队列中
-
等待浏览器异步任务队列执行 callbacks 数组中的 flushSchedulerQueue 方法。
-
每个 flushSchedulerQueue 方法中先将 queue 队列排序,再遍历 queue 执行 watcher.before() 和 watcher.run() 方法,而后再初始化异步更新队列,自此异步更新完成。
2.Vue 的 nextTick 全局 API 是如何实现的?
Vue.nextTick 将传递的刷新 watcher 队列的回调函数 用 try catch 包裹然后放入 callbacks 数组。 在浏览器异步任务队列无其他刷新 callbacks 数组的方法时,执行 timerFunc 函数,放入当前刷新 callbacks 数组的方法。进而达到在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM 的功能
3.Vue 是如何将刷新 callbacks 数组的函数放入浏览器任务队列进行异步更新的?
根据浏览器任务队列异步执行的效率来选择放入方法的优先级,分别为:
-
Promise.resolve().then(flushCallbacks)
-
new MutationObserver(flushCallbacks)
- 提供了监视对DOM树所做更改的能力(HTML5 中的新特性)
-
setImmediate(flushCallbacks)
-
setTimeout(flushCallbacks, 0)