一起养成写作习惯!这是我参与「掘金日新计划 · 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渲染。