js任务队列和nextTick

本文主要介绍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>

当我第一次点击按钮时,它打印的结果是:


2.png

当我第二次点击按钮时,它打印的结果是:


3.png

诶,这就很有意思了,为什么我明明先给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)
      })
    }

执行结果:


4.png
为什么添加了this.$nextTick就能正常获取值了呢?

this.nextTick的作用是什么呢?官网中是这样介绍的:在下次 DOM 更新循环结束之后执行延迟回调。可能有点不太懂。 这样说吧,什么叫tick,就是每次执行一次微任务队列里的所有的任务叫tick,this.nextTick的作用就是将它里面的任务添加到微任务队列的末尾。
所以,等this.$nextTick任务执行时,watch的回调函数,组件的更新,生命周期钩子函数它们都执行完了,也就能正常获取值了。

还有一个小问题,为什么组件的更新要设置为异步更新呢?

当我们用js操作DOM的时候,对DOM进行100次操作,就需要更新100次DOM,而在vue里面,我们都是操作虚拟DOM,当我操作100次虚拟DOM时,更新DOM只需要1次,这样就能大大地提升性能。

如有错误,欢迎指正!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容