一、NextTick是什么
官方对其的定义
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
什么意思呢?
我们可以理解成,Vue
在更新 DOM
时是异步执行的。当数据发生变化,Vue
将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再 统一进行更新
举例一下
Html
结构
构建一个vue
实例
修改message
这时候想获取页面最新的DOM
节点,却发现获取到的是旧值
这是因为message
数据在发现变化的时候,vue
并不会立刻去更新Dom
,而是将修改数据的操作放在了一个异步操作队列中
如果我们一直修改相同数据,异步操作队列还会进行去重
等待同一事件循环中的所有数据变化完成之后,会将队列中的事件拿来进行处理,进行DOM
的更新
为什么要有nextTick
举个例子
如果没有 nextTick
更新机制,那么 num
每次更新值都会触发视图更新(上面这段代码也就是会更新10万次视图),有了nextTick
机制,只需要更新一次,所以nextTick
本质是一种优化策略
二、使用场景
如果想要在修改数据后立刻得到更新后的DOM
结构,可以使用Vue.nextTick()
- 第一个参数为:回调函数(可以获取最近的
DOM
结构)
- 第二个参数为:执行函数上下文
组件内使用 vm.$nextTick()
实例方法只需要通过this.$nextTick()
,并且回调函数中的 this
将自动绑定到当前的 Vue
实例上
$nextTick()
会返回一个 Promise
对象,可以是用async/await
完成相同作用的事情
三、实现原理
nextTick 主要使用了 宏任务和微任务。根据执行环境分别尝试采用 Promise、MutationObserver、setImmediate 如果以上都不行则采用 setTimeout。
Vue 在更新 Dom 时是异步执行的,只要监听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。nextTick 方法会在队列中加入一个回调函数,确保该函数在前面的 Dom 操作完成后才调用。
源码位置:/src/core/util/next-tick.js
callbacks
也就是异步操作队列
callbacks
新增回调函数后又执行了timerFunc
函数,pending
是用来标识同一个时间只能执行一次
timerFunc
函数定义,这里是根据当前环境支持什么方法则确定调用哪个,分别有:
Promise.then
、MutationObserver
、setImmediate
、setTimeout
通过上面任意一种方法,进行降级操作
无论是微任务还是宏任务,都会放到flushCallbacks
使用
这里将callbacks
里面的函数复制一份,同时callbacks
置空
依次执行callbacks
里面的函数
小结:
- 把回调函数放入callbacks等待执行
- 将执行函数放到微任务或者宏任务中
- 事件循环到了微任务或者宏任务,执行函数依次执行callbacks中的回调
总结
因为 Vue 采用的异步更新策略,当监听到数据发生变化的时候不会立即去更新 DOM,而是开启一个任务队列,并缓存在同一事件循环中发生的所有数据变更。
this.$nextTick()
方法主要是用在随数据改变而改变的 dom 应用场景中,Vue 中数据和 dom 渲染由于是异步的,所以,要让 Dom 结构随数据改变这样的操作都应该放进 this.$nextTick()
的回调函数中。
简单的理解,vue.js 中 this.$nextTick() 就是起到了一个等待数据的作用,也就是说,将一些回调延迟,等到 DOM 更新之后再开始执行。
扩展阅读