等待下一次 DOM 更新刷新的工具方法。
简单地说nextTick()
可以在当前代码块的同步代码执行完毕后,立即执行传入的回调函数。
通常情况下,Vue 在更新 DOM 时是异步执行的。当数据发生变化,Vue将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新,这意味着如果你想在 DOM 更新后立即执行一些操作,可能会遇到问题。这时就可以使用 nextTick()
方法来确保回调函数在 DOM 更新后立即执行。
需要注意的是,nextTick()
方法是异步的,所以回调函数的执行顺序是不确定的。如果你需要确保回调函数在 Vue 实例更新后立即执行,并且需要控制回调函数的执行顺序,可以使用 Vue.nextTick().then()
方法。
理解nextTick()首先得从JS执行机制开始
我们都知道 JS 是单线程
语言,即指某一时间内只能干一件事
。
那为什么 JS 不能是多线程呢?多线程就能同一时间内干多件事情了
一个很简单的例子,如果同一时间,一个添加了 DOM,一个删除了 DOM, 这个时候语言就不知道是该添还是该删了,所以从应用场景来看 JS 只能是单线程
单线程就意味着我们所有的任务都需要排队,后面的任务必须等待前面的任务完成才能执行。如果前面的任务耗时很长,一些从用户角度上不需要等待的任务就会一直等待,这个从体验角度上来讲是不可接受的,所以JS中就出现了异步
的概念
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步
1、 当 Vue 组件数据发生变化时,DOM 会异步更新。Vue 从所有组件收集对虚拟 DOM 的多次更新,然后创建一个批次来更新 DOM。
在单个批次中更新 DOM 比进行多个小更新的性能更高。
当数据发生变化时,Vue 会将需要更新的组件放入更新队列中,并在下一个事件循环中进行批量更新。这样可以避免频繁的 DOM 操作,提高性能。
下面是一个简单的例子来演示这个机制:
<div id="app">
<p>{{ message }}p>
<button @click="changeMessage">Change Messagebutton>
div>
new Vue({
el: "#app",
data: {
message: "Hello Vue!"
},
methods: {
changeMessage() {
this.message = "Updated Message";
console.log("Message changed");
}
}
});
在上述例子中,当点击按钮时,changeMessage
方法会将 message
的值修改为 “Updated Message”。同时,在控制台中输出 “Message changed”。
由于 Vue 的批量更新机制,当点击按钮时,实际的 DOM 更新会在下一个事件循环中进行。这意味着控制台中的输出会先执行,而实际的 DOM 更新稍后进行。
这种批量更新的机制可以减少不必要的 DOM 操作,提高性能和响应速度。
2、 当我们可以使用 settimeout 时,可以使用nextTick。
Vue 异步更新 DOM。因此,当您对 DOM 进行更改时,它不会立即发生。 它首先检查是否有任何其他状态更改。只有这样您才能在浏览器上看到呈现的更改!
这一切发生得如此之快,你甚至都没有看到。 那么,为什么它很重要?
这确实很重要,如果您需要在进行更改后立即运行一个函数。这是你需要用来nextTick()等待 DOM 更新的地方。
这就是为什么在某些时候你必须使用setTimeout(),因为你必须给浏览器一些时间来更新 DOM。否则,你的函数没有被执行。
但setTimeout()有其后备方案。它将在nextTick(DOM更新)之后执行回调,同时nextTick()优先执行回调函数!setTimeout()延迟您的回调,因为它首先必须通过使用它来将控制权交给浏览器,然后只有在调用您的回调函数时才返回给您。
举个例子:
<template>
<h1>{{ message }}</h1>
</template>
<script>
export default {
data() {
return {
message: "Joey doesn’t share food!",
};
},
mounted() {
console.log(this.message);
this.message =
"Well, maybe I don't need your money. Wait, wait, I said maybe!";
console.log(this.message);
setTimeout(() => {
this.message = "Hi, I’m Chandler. I make jokes when I’m uncomfortable.";
console.log(this.message);
}, 300);
this.$nextTick(() => {
this.message =
"It's a moo point. It's like a cow's opinion; it doesn't matter. It's moo.";
console.log(this.message);
});
},
};
</script>
结果如下:
可以看见next Tick在setTimeout之前运行,这就是nextTick性能高的原因
那为什么不用来watch()监听变化呢?简而言之,watch()用于在组件数据更改时执行某些操作,而nextTick()用于在应用程序更新后执行代码
3、回调函数作为第一个参数
mounted () {
this.$nextTick(() => {
this.message = 'Call me maybe!';
console.log(this.message);
});
}
在上述代码中,mounted()
会在组件挂载到 DOM 后被调用。
在这个例子中,我们将 message
的值修改为 “Call me maybe!”,然后在控制台中打印出 message
的值。
使用 this.$nextTick()
方法可以确保在 DOM 更新后执行回调函数。这是因为 Vue 在更新 DOM 后会异步执行回调函数,所以如果我们直接在 mounted()
中修改 message
的值并打印,可能会得到旧的值。通过使用 this.$nextTick()
,我们可以确保回调函数在 DOM 更新后执行,从而得到正确的值。
当您想要执行某些操作并确保更新子组件的 props、数据或计算值时,回调会非常方便。您的代码会在下一个 DOM 更新周期运行后以及浏览器呈现该更改后延迟。
换句话说,回调被推迟到下一个 DOM 更新周期之后执行。
4、this.$nextTick().then()
mounted () {
this.$nextTick().then(() => {
this.message = 'You promised!';
console.log(this.message);
});
}
使用 this.$nextTick().then()
的方式可以确保回调函数在 DOM 更新后执行。由于 this.$nextTick()
返回一个 Promise 对象,我们可以使用 .then()
方法来注册回调函数,并在 Promise 被解析时执行这个回调函数。
5、async 与 this.$nextTick()
async mounted () {
await this.$nextTick(() => {
this.message = 'I will always wait for you!';
console.log(this.message)
});
}
在上述代码中,我们使用了 async
和 await
关键字来确保在 DOM 更新后执行一些操作。
在 mounted()
钩子函数中,我们使用 await
关键字来等待 this.$nextTick()
方法的执行。await
关键字可以暂停异步函数的执行,直到 this.$nextTick()
返回的 Promise 对象被解析。
通过使用 async
和 await
,我们可以将异步代码写成同步的形式,使得代码更加清晰和易读。