一个前端开发者(假设他的名字是Eric)走进了一家Vue酒吧。
Eric点了他最喜欢的鸡尾酒:Nuxt。调酒师正在调酒。然后他开始咆哮。
他开始说他是如何在Vue 3文档中的实例方法下发现$nextTick ,并被震撼了。Eric使用Vue已经有一段时间了,他习惯于把$watch 和$emit 写成实例方法。那么,$nextTick 有什么用呢?Vue文档说,它"[推迟]回调,在下一个DOM更新周期后执行"。
但Eric并不相信。
他继续叙述了他是如何尝试这样做的。
this.loadingAnimation = true
this.startVeryLongCalculation()
this.completeVeryLongCalculation()
this.loadingAnimation = false
但浏览器什么也没显示。他去了StackOverflow,有人推荐使用$nextTick 。这个人说,"它推迟了回调",但至少他们包括一个代码片段。埃里克把他的代码修改成这样。
this.loadingAnimation = true
this.$nextTick(() => this.startVeryLongCalculation())
this.endVeryLongCalculation()
this.loadingAnimation = false
它成功了。为什么?
在这篇文章中,如果你和Eric有类似的情况,你就会明白nextTick 是如何工作的,并看到用一个真实的用例证明。
[H2] 先决条件
你应该有以下的基本知识。如果你不知道,请回顾这些主题。
- 事件循环
- 回调函数
- 微任务、队列和时间表
- 异步更新队列
[H2]nextTick是做什么的?
nextTick 接受一个回调函数,它被延迟到下一个DOM更新周期。它只是Vue的一种说法:"嘿,如果你想在DOM更新后执行一个函数(这种情况很少发生),我希望你使用nextTick 而不是setTimeout"。
Vue.nextTick(() => {}) // syntax
我们很快就会讨论setTimeout 与nextTick 的争论。让我们通过这个例子来直观地了解nextTick 的行为。
<template>
<div>
{{ currentTime }}
</div>
</template>
<script>
export default {
name: 'getCurrentTime',
data() {
return {
currentTime: ''
}
},
mounted() {
this.currentTime = 3;
this.$nextTick(() => {
let date = new Date()
this.currentTime = date.getFullYear()
});
}
}
</script>
注意:
this.$nextTick与全局API方法vue.nextTick相同,只是回调函数的this被自动绑定到调用它的实例上。
在JSFiddle或你的电脑上运行这个代码片段。它将显示2021 。并不是说如果你删除nextTick ,就不会得到同样的结果。然而,你应该明白,Vue是根据数据中的内容对DOM进行修改的。
在上面的代码片段中,Vue将DOM更新为3 ,然后调用回调,将DOM更新为2021 ,最后将控制权交给浏览器,显示2021 。
到目前为止,我们已经探讨了nextTick ,在回调队列中插入回调函数,并在合适的时候执行该函数的部分。
这就是你需要知道的一切。
但你会有兴趣知道,nextTick 中的回调是作为事件循环中的一个微任务使用的。nextTick的源代码明确指出:"nextTick行为利用了微任务队列,它可以通过本地Promise.then 或MutationObserver 。"
[H3]se``tTimeout 对比。nextTick
在DOM被更新后执行一个函数的另一种方式是使用JavaScriptsetTimeout() 函数。
让我们在上面同样的代码例子中用setTimeout 替换nextTick 。
<template>
<div>
{{ currentTime }}
</div>
</template>
<script>
export default {
name: 'getCurrentTime',
data() {
return {
currentTime: ''
}
},
mounted() {
this.currentTime = 3;
setTimeout(() => {
let date = new Date()
this.currentTime = date.getFullYear()
}, 0);
}
}
</script>
在你的本地服务器或这个JSFiddle上运行这个代码片段。你会先看到3 ,然后是2021 。它发生得很快,所以如果你一开始没有看到这种行为,你可能需要刷新浏览器。
在上面的代码片段中,Vue将DOM更新为3 ,并给浏览器控制权。然后浏览器显示3 ,调用回调,将DOM更新为2021 ,最后将控制权交给浏览器,现在浏览器显示2021 。
nextTick 的实现将setTimeout 作为最后的备用方法,用于无法使用Promise 和MutationObserver 的浏览器(IE 6-10 和 Opera Mini 浏览器)。对于不支持Promise 和MutationObserver 的浏览器(IE 10),它甚至更倾向于使用setImmediate 。
只有Opera Mini浏览器不具备这三种方法,而不得不使用setTimeout 。
[H2] 何时使用nextTick
很少有情况需要你拿出大的nextTick 。其中一些情况是。
- 当您想使用
setTimeout - 当你想非常确定DOM能反映你的数据时
- 当你在尝试执行异步操作时遇到错误,如
Uncaught (in promise) DOMException。记住,Vue是异步更新DOM的
让我们通过一个使用Vue 3的最后例子。
<div id="app">
<div ref="listScroll" class="scrolledList">
<ul ref="scrolledHeight">
<li v-for="month in months">
{{month}}
</li>
</ul>
</div>
<input type="text" placeholder="Add Month" v-model="month">
<button @click="addMessage" @keyup.enter="addMessage"> Add Month</button>
</div>
<script src="https://unpkg.com/vue@next">
Vue.createApp({
data() {
return {
month: '',
months: ['Jan', 'Feb', 'Apr', 'May', 'June', 'July', 'Aug']
}
},
mounted() {
this.updateScrollNextTick()
},
methods: {
addMessage() {
if(this.month == ''){
return
}
this.months.push(this.month)
this.month = ''
this.updateScrollNextTick()
},
updateScrollNextTick () {
let scrolledHeight = this.$refs.scrolledHeight.clientHeight
this.$nextTick(() => {
this.$refs.listScroll.scrollTo({
behavior: 'smooth',
top: scrolledHeight
})
})
}
},
})
.mount("#app")
</script>
在你的本地机器或CodePen上运行这个。你应该得到像这样的东西。

在上面的代码片段中,我们想在一个新项目被添加到列表中时获得平滑的向下滚动效果。浏览一下代码,尝试修改一下,删除nextTick ,你会失去那种平滑的滚动效果。你也可以尝试用setTimeout 来代替nextTick 。
[H2] 结语
在这篇文章中,我们已经探讨了nextTick 是如何工作的。我们进一步了解了它与vanilla JavascriptsetTimeout 的不同之处,并涵盖了实际的使用案例。
前面提到的实例方法很少需要,所以如果你最近不得不使用它,请在这篇文章下留言,分享你的经验。
The postUnderstanding $nextTick in Vue.js appearedfirst on LogRocketBlog.