Vue.nextTick的原理和用途

248 阅读3分钟

一、原理

1.异步说明

Vue实现响应式并不是数据发生变化之后DOM立即变化,而是按一定的策略进行DOM的更新。

2.事件循环说明

简单来说,Vue在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。

二、Vue.nextTick的机制

1.为什么要用Vue.nextTick()

Js执行是单线程的,它是基于事件循环的。

事件循环⼤致分为以下⼏个步骤:

(1)所有同步任务都在主线程上执⾏, 形成⼀个执⾏栈(execution context stack)。

(2)主线程之外, 还存在⼀个"任务队列"(task queue) 。 只要异步任务有了运⾏结果, 就在"任务队列"之中放置⼀个事件。

(3) ⼀旦"执⾏栈"中的所有同步任务执⾏完毕, 系统就会读取"任务队列", 看看⾥⾯有哪些事件。 那些对应的异步任务, 于是结束等待状态, 进⼊执⾏栈, 开始执⾏。

(4) 主线程不断重复上⾯的第三步。

2.什么是Vue.nextTick()

是Vue的核心方法之一,官方文档解释如下:

在下次DOM更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM。

源码浅析

nextTick的实现单独有一个JS文件来维护它,在src/core/util/next-tick.js中。

nextTick的源码主要分为两块:能力检测和根据能力检测以下不同方式执行回调队列。

作用

nextTick就是设置一个回调,用于异步执行。

简单的理解是:当数据更新了,在dom中渲染后,自动执行该函数,

<template>
  <div class="hello">
    <div>
      <button id="firstBtn" @click="testClick()" ref="aa">{{testMsg}}</button>
    </div>
  </div>
</template>
 
<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      testMsg:"原始值",
    }
  },
  methods:{
    testClick:function(){
      let that=this;
      that.testMsg="修改后的值";
      console.log(that.$refs.aa.innerText);   //that.$refs.aa获取指定DOM,输出:原始值
    }
  }
}
</script>

3.怎么用Vue.nextTick()

语法Vue.nextTick([callback,context])

参数

  • {Function} [callback]:回调函数,不传时提供promise掉使用
  • {Object}[context]:回调函数执行的上下文环境,不传默认是自动绑定到调用它的实例上。
//改变数据
vm.message = 'changed'

//想要立即使用更新后的DOM。这样不行,因为设置message后DOM还没有更新
console.log(vm.$el.textContent) // 并不会得到'changed'

//这样可以,nextTick里面的代码会在DOM更新后执行
Vue.nextTick(function(){
    // DOM 更新了
    //可以得到'changed'
    console.log(vm.$el.textContent)
})

// 作为一个 Promise 使用 即不传回调
Vue.nextTick()
  .then(function () {
    // DOM 更新了
  })

Vue实例方法 vm.$nextTick做了进一步封装,把context参数设置成当前Vue实例。

4.小结

使用 Vue.nextTick() 是为了可以获取更新后的DOM。

触发时机:在同一事件循环中的数据变化后,DOM完成更新,立即执行 Vue.nextTick() 的回调。

同一事件循环中的代码执行完毕 -> DOM 更新 -> nextTick callback触发

v2-4c5d4f1d8ebd197ce9a58038d86e9bf8_r.jpg

应用场景

  • 在Vue生命周期的 created() 钩子函数进行的DOM操作一定要放在 Vue.nextTick() 的回调函数中。原因:是 created() 钩子函数执行时DOM其实并未进行渲染。
  • 在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作应该放在*Vue.nextTick()*的回调函数中。原因: Vue异步执行Dom更新,只要观察到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变,如果同一个watcher被多次出发,只会被推入到队列中一次。