1. 作用
将值赋给data后是不立即更新的。这时候就需要用nextTick对更新后的dom节点做操作
2. 使用
this.$nextTick(function () {
console.log(this.$el.textContent) // => '已更新'
})
await this.$nextTick()
// => '已更新'
3. 原理
为了避免不必要的计算和 DOM 操作,vue将DOM更新设计为异步。nextTick是一个微任务,返回一个promise,可以对更新后的dom进行一些操作。
注意:这里不需要关心事件循环中的渲染。就像下面示例,图片被alert阻塞未渲染,这已经能拿到高度了。
<script>
let imgDom = document.createElement("img");
imgDom.setAttribute("id", "myimg");
imgDom.setAttribute("width", "200px");
imgDom.setAttribute("height", "200px");
imgDom.setAttribute("src", "https://ss0.baidu.com/7Po3dSag_xI4khGko9WTAnF6hhy/zhidao/pic/item/c995d143ad4bd113fceedf775bafa40f4bfb0557.jpg");
let body = document.querySelector("body");
console.log("附加前高度", body.offsetHeight);//400
body.append(imgDom);
console.log("附加前高度", body.offsetHeight);//600
alert('暂停')
</script>
4. nextTick 为什么是 next tick?
从字面意思理解,next 下一个,tick 滴答(钟表)来源于定时器的周期性中断(输出脉冲),一次中断表示一个 tick,也被称做一个“时钟滴答”,nextTick 顾名思义就是下一个时钟滴答,下一个任务。下一个任务,在 Event Loop 中在熟悉不过了通过一个例子简单回忆一下 Event Loop。
console.log('同步代码1');
setTimeout(() => {
console.log('setTimeout')
}, 0)
new Promise((resolve) => {
console.log('同步代码2')
resolve()
}).then(() => {
console.log('promise.then')
})
console.log('同步代码3');
// 最终输出"同步代码1"、"同步代码2"、"同步代码3"、"promise.then"、"setTimeout"
第一个 tick(图例中第一个步骤,即’本次更新循环’)
首先修改数据,这是同步任务。同一事件循环的所有的同步任务都在主线程上执行,形成一个执行栈,此时还未涉及 DOM 。
Vue 开启一个异步队列,并缓冲在此事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。
同步任务在主线程执行,这是第一个 task。
第二个 tick(图例中第二个步骤,即’下次更新循环’)
同步任务执行完毕,开始执行异步 watcher 队列的任务,更新 DOM 。Vue 在内部尝试对异步队列使用原生的 Promise.then 和 MO,如果执行环境不支持,会采用 setImmediate 或者是 setTimeout(fn, 0) 代替(不同 Vue 版本 API 不一样)。
DOM 更新是第二个 task 。
第三个 tick(图例中第三个步骤)
当 DOM 更新循环结束之后,此时调用下一个 task 执行,也就是 nextTick 中注册的延迟回调 。$nextTick 其实和第二个 task 是一样的操作,但是属于不同的 task。
也就是第三个 task。