浅谈vue的nextTick和render的顺序

361 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情

前言

经常用vue的同学们应该都知道,vue是数据驱动页面的,数据更新后,页面也能及时响应更新。这个过程是异步的,并不是实时更新的。

我们一般不需要知道这个过程。但是有时候需要,在页面的dom更新后对dom做处理。所以vue就提供了一个nextTick函数。

在这个函数里面的回调里面我们可以拿到更新后的dom元素,然后进行处理。

举个例子:

<template>
  <div ref="div" @click="setName">{{name}}</div>
</template>
<script>
export default {
  data () {
    return {
      name: '佚名'
    }
  },
  methods: {
    setName () {
      this.name = '答案cp3'
      console.log(this.$refs.div.innerText) // 佚名
      this.$nextTick(() => {
        console.log(this.$refs.div.innerText) // 答案cp3
      })
    }
  }
}
</script>

可以看到设置了name之后,马上去取dom的innerText,还是佚名

如果我们在nextTick后取dom的innerText,则就变成答案cp3

这个过程,大家应该都很熟悉。

但是我有个疑问,我们知道vue的nextTick内部是优先采用Promise来实现的(如果你的环境支持Promise), Promise是一个微任务, 然后页面的渲染(render)是宏任务。

附上宏任务/微任务

宏任务微任务
script整体代码promise的then, async与await
setImmediate(Node)process.nextTick(Node)
requestAnimationFrame(动画api,动画帧)MutationObserver(监听dom节点变化)
setTimeout/setInterval
I/O 操作,页面的渲染(render)

按照微任务优先宏任务的执行逻辑,页面的渲染应该在nextTick之后,为什么在nextTick的回调就可以获取更新后的dom元素?

分析

这里需要区分下,更新dom元素页面的render是两个操作,我们是先更新dom,再渲染(render)到页面上。

我们进行vue数据赋值的时候,这时候会触发watcher对象的update方法,把watcher对象都push到queue队列中。

然后使用nextTick执行flushSchedulerQueue函数。在这个flushSchedulerQueue函数里面,会遍历上面queue队列,执行watcher对象的run方法,更新dom元素。

然后我们也是使用的nextTick函数去获取更新后的dom元素。

nextTick函数内部也是使用队列存储这些回调函数。

执行nextTick时,会依次执行存入队列的函数,会先执行flushSchedulerQueue函数,更新dom元素。

然后再执行我们获取最新的dom的回调。这时候就能拿到最新的dom元素了。

最后再执行页面的render渲染。