this.$nextTick()原理及其使用

461 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天 [携手创作,共同成长]

this.$nextTick()

首先在深入学习this.nexTick之前我们要去了解一个概念,就是vue的dom更新他是异步的,并不是数据发生改变之后dom就立即改变,而是等同一事件循环中的所有数据变化完成之后,在统一进行视图的更新。

1、this.$nextTick()使用的场景:

created阶段的dom是没有生成渲染的,这个时候我们要在这个生命周期里面获取dom的话,就可以使用nextTick方法就获取

mounted阶段,如果需要操作渲染后的视图,要使用nextTick方法,因为在mounted钩子里面不会承诺其子组件也挂载完毕

2、this.$nextTick()原理及其使用

官方给的解释是:

将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。

还是很难理解的,这里通过代码的实例带你去领略:

<template>
    <div>
        <p ref="testDom">{{name}}</p>
        <button @click="change">改变</button>
    </div>
</template>
<script>
export default {
    data() {
        return {
            name:'前端同学'
        }
    },
    created() {
        
    },
    methods: {
        change(){
            this.name='我是测试同学'
            console.log(this.$refs.testDom.innerHTML);
            this.$nextTick(()=>{
                console.log(this.$refs.testDom.innerHTML);
            })
        }
    },
}
</script>
好的这就是我们整个的一个代码,逻辑也很简单,就是点击按钮去改变name值,让其在页面上展示出来,再点击改变的这个事件里面有两个输出,都是通过获取dom的方式去获取到元素里面的内容,但是在浏览器上打印确实不同的,控制台上的输出是:
    前端同学
    我是测试同学
这里就有疑问了 明明我已经改变了name的值,而我的输出还是‘前端同学’呢?

通过上面的例子我们就不难得出结论了,大概也明白this.nextTick是怎么使用了,但是其中的原理是什么,我总结了如下几条,代表个人理解:

  1. dom更新:在vue中,你修改了data的某一个值,并不会立即反应到该ele中。vue将你对data的更 改放到watcher的一个对列中(异步),只有在当前任务空闲时才会去执行watcher队列任务。这就有一个延迟时间了。
  2. 当执行到nextTick的时候,这是一个异步任务,异步任务是不会立即执行的,会被js处理器放到一个队列里,按照队列的顺序优先级等一个个按照次序执行,新添加的事件都会放在队列末尾。所以,当第一个也就是data的修改执行渲染在页面之后,这个时候执行nextTick,就肯定能获取dom。(这里涉及到了js的事件执行机制,在以后的文章中我也会更新)

3、this.$nextTick与setTimeout的区别

this.$nextTick()优先于setTimeout执行的,这两者是分属不同队列的,nextTick 在vue 源码中是利用 Promise.resolve()实现的

this.nextTick(),Promise是属于微队列

setTimeout ; setInterval ; setImmediate是属于宏队列

这就不难理解this.$nextTick()优先于setTimeout执行,因为js是单线程的,在执行js代码的时候, 全局的js代码是个宏任务, 遇到同步的就执行,异步的就分别放入对应的任务队列中去; 等到全局的js代码的同步执行完成后,就开始找微队列里的微任务,等到微任务执行完成后,才开始执行宏队列的宏任务;

总结:

Vue的特点之一就是能实现响应式,但数据更新时,DOM不会立即更新,而是放入一个异步队列中,因此如果在我们的业务场景中,有一段代码里面的逻辑需要在DOM更新之后才能顺利执行,这个时候我们可以使用this.$nextTick() 函数来实现。