相信大家在写vue项目的时候,一定会发现一个神奇的api,Vue.nextTick
。为什么说它神奇呢,那是因为在你做某些操作不生效时,将操作写在Vue.nextTick
内,就神奇的生效了。那这是什么原因呢?
让我们一起来研究一下。
简述
-
vue 实现响应式并不是数据发生变化后 DOM 立即变化,而是按照一定策略异步执行 DOM 更新的
-
vue 在修改数据后,视图不会立刻进行更新,而是要等同一事件循环机制内所有数据变化完成后,再统一进行DOM更新
-
nextTick
可以让我们在下次 DOM 更新循环结束之后执行延迟回调,用于获得更新后的 DOM。
事件循环机制
在讨论Vue.nextTick
之前,需要先搞清楚事件循环机制,算是实现的基石了,那我们来看一下。
在浏览器环境中,我们可以将我们的执行任务分为宏任务和微任务,
- 宏任务: 包括
整体代码script
,setTimeout
,setInterval
、setImmediate
、 I/O 操作、UI 渲染 - 微任务:
Promise.then
、MuationObserver
事件循环的顺序,决定js代码的执行顺序。事件循环如下:
用代码解释,浏览器中事件循环的顺序同如下代码:
for (macroTask of macroTaskQueue) {
// 1. 执行一个宏任务
handleMacroTask();
// 2. 执行所有的微任务
for (microTask of microTaskQueue) {
handleMicroTask(microTask);
}
}
vue数据驱动视图的处理(异步变化DOM)
<template>
<div>
<div>{
{
count}}</div>
<div @click="handleClick">click</div>
</div>
</template>
export default {
data () {
return {
number: 0
};
},
methods: {
handleClick () {
for(let i = 0; i < 10000; i++) {
this.count++;
}
}
}
}
分析上述代码:
参考vue实战视频讲解:进入学习
- 当点击按钮时,count会被循环改变10000次。那么每次count+1,都会触发count的
setter
方法,然后修改真实DOM。按此逻辑,这整个过程,DOM会被更新10000次,我们都知道DOM的操作是非常昂贵的,而且这样的操作完全没有必要。所以vue内部在派发更新时做了优化 - 也就是,并不会每次数据改变都触发 watcher 的回调,而是把这些 watcher 先添加到一个队列queueWatcher里,然后在 nextTick 后执行 flushSchedulerQueue处理
- 当 count 增加 10000 次时,vue内部会先将对应的 Watcher 对象给 push 进一个队列 queue 中去,等下一个 tick 的时候再去执行。并不需要在下一个 tick 的时候执行 10000 个同样的 Watcher 对象去修改界面,而是只需要执行一个 Watcher 对象,使其将界面上的 0 变成 10000 即可</