一、定义

vue官方定义:

Vue.nextTick([callback,context]),在下次DOM更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新之后的DOM。

nextTick是前端面试中vue框架中必考的部分,一定要掌握。它主要是处理我们再变更完数据以后,无法立刻拿到最新的DOM节点对象的问题。我们可以这样理解:vue执行完渲染后会执行this.nextTick()里面的callback函数。

二、实例

对于上面的定义,可能不太好理解,那么举一个小例子:

<div id="app">
     <p id="msg">{{message}}</p>
     <button @click="change">更新</button>
</div>
var app = new Vue({
    el: '#app',
    data: {
        message: 'Hello Vue!'
    },
    methods:{
        change(){
            this.message = 'This is new message!';
            console.log('not nextTick():',document.getElementById("msg").innerHTML);
        }
    }
})

预期结果,vue是响应式的,数据改变会驱动视图改变。所以,应该输出:

not nextTick(): This is new message!

可实际输出却是:

在这里插入图片描述

这是为什么呢?页面上视图都更新了,为什么获取到的值还是原来的呢?

Vue在更新DOM的时候,是异步执行的。只要监听到数据变化,vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。

通俗的说,就是vue data中的数据更新时,它不会立即去渲染页面,也就是修改页面中的dom,而是先存储起来,如果一个数据被改了很多次,以最后一次修改为准,这就是所谓的异步更新DOM。

而我们在改变message之后,直接获取DOM,等于是和数据改变事件同步进行,此时页面上的DOM仍然未更新,获取到的还是原来的“Hello Vue!”旧数据。这也是为什么vue不建议我们直接操作dom的原因之一。

接下来我们使用nextTick解决问题。

三、使用nextTick()

var app = new Vue({
   el: '#app',
   data: {
       message: 'Hello Vue!'
   },
   methods:{
       change(){
           this.message = 'This is new message!';
           this.$nextTick(()=>{
               console.log('use nextTick():',document.getElementById("msg").innerHTML);
           })
           console.log('not nextTick():',document.getElementById("msg").innerHTML);
       }
   }
})

输出:

在这里插入图片描述

值得注意的是,即使this.$nextTick写在了console.log('not nextTick():',document.getElementById("msg").innerHTML);前面,但是this.$nextTick中回调函数的输出仍然在后面,这更一步说明了这个输出是异步执行的。

那么,使用setTimeout可以吗?

我们修改js代码:

var app = new Vue({
    el: '#app',
    data: {
        message: 'Hello Vue!'
    },
    methods:{
        change(){
            this.message = 'This is new message!';
            setTimeout(()=>{
                console.log('use nextTick():',document.getElementById("msg").innerHTML);
            })
            console.log('not nextTick():',document.getElementById("msg").innerHTML);
        }
    }
})

结果:

在这里插入图片描述

可以看到,和使用nextTick效果是一样的,因为setTimeout中的回调函数也是在本次执行栈清空后被调用的,可以参考:
深入学习JS:任务队列和事件循环

js中各异步调用的执行顺序:

同步代码 > nextTick > Promise> setTimeout

由于

vue是在下一个的事件循环“tick”中,刷新队列并执行实际 (已去重的) 工作。

所以setTimeout中输出函数获取到的值肯定是更新以后的值。

所以总结一下,使用this.$nextTick()之所以能获取到更新后的值,并不是改变了vue的渲染流程,而是改变了获取最新值的时间,并不是立即获取,而是等vue渲染完后再获取,即异步获取

Logo

基于 Vue 的企业级 UI 组件库和中后台系统解决方案,为数万开发者服务。

更多推荐