本文主要介绍vue的任务队列和nextTick,其中也涉及宏任务和微任务。
好,现在开始,先看一个比较有意思的东西:
<template>
<div>
<div style="width: 50px" ref="msgRef">{{ msg }}</div>
<button @click="btnClick">加内容</button>
</div>
</template>
<script>
export default {
data() {
return {
msg: ''
}
},
methods: {
btnClick() {
this.msg += '哈哈哈哈哈哈哈哈哈哈哈哈'
console.log('msgdiv的offsetHeight', this.$refs.msgRef.offsetHeight)
}
}
}
</script>
<style lang="less" scoped></style>
当我第一次点击按钮时,它打印的结果是:
当我第二次点击按钮时,它打印的结果是:
诶,这就很有意思了,为什么我明明先给msg添加了内容,再打印高度,但是打印的高度还是上一次的高度呢?先说原因:是由于异步事件引起的。
ok,接下来就是解答时间,当然解答前还是要说一下,js的事件队列:
当我们在执行一段代码时,浏览器会开启一个线程用于处理任务----主线程(当然,js是单线程),浏览器会按照顺序将同步任务一个一个放进主线程中,按照先进先出的顺序执行任务。当遇到异步任务的时候,就会开启一个事件队列,将异步任务挂起,当异步任务有返回结果了,就将该异步任务加入到事件队列中,当主线程空闲了(当主线程中所有同步任务都执行完毕),就将事件队列中的任务按照先进先出的顺序添加到主线程中。主线程就重复以上步骤。
说到异步任务,就不得说一说异步事件当中也分两类:宏任务和微任务,微任务先于宏任务执行。
宏任务:DOM事件(点击事件),setTimeout,setInterval等等
微任务:promise的回调函数,nextTick,watch的回调函数,组件的更新,生命周期钩子函数等等
好,言归正传,为什么是由于异步事件引起的?
因为当我们给msg赋值的时候,这其中会触发一系列的事件-----watch的回调函数,组件的更新,生命周期钩子函数,它们都是异步任务,而【this.msg += '哈哈哈哈哈哈哈哈哈哈哈哈',console.log('msgdiv的offsetHeight', this.$refs.msgRef.offsetHeight)】这两句代码都是同步任务,会执行在异步任务之前,所以获取的结果是还未更新的结果。
那要怎么解决呢?
btnClick() {
this.msg += '哈哈哈哈哈哈哈哈哈哈哈哈'
this.$nextTick(() => {
console.log('msgdiv的offsetHeight', this.$refs.msgRef.offsetHeight)
})
}
执行结果:
为什么添加了nextTick就能正常获取值了呢?
nextTick的作用是什么呢?官网中是这样介绍的:在下次 DOM 更新循环结束之后执行延迟回调。可能有点不太懂。
这样说吧,什么叫tick,就是每次执行一次微任务队列里的所有的任务叫tick,nextTick的作用就是将它里面的任务添加到微任务队列的末尾。
所以,等nextTick任务执行时,watch的回调函数,组件的更新,生命周期钩子函数它们都执行完了,也就能正常获取值了。
还有一个小问题,为什么组件的更新要设置为异步更新呢?
当我们用js操作DOM的时候,对DOM进行100次操作,就需要更新100次DOM,而在vue里面,我们都是操作虚拟DOM,当我操作100次虚拟DOM时,更新DOM只需要1次,这样就能大大地提升性能。
如有错误,欢迎指正!