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