一、原理
1、异步
Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。
2、事件循环
简单来说,Vue在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。
二、Vue.nextTick的机制
1、为什么用Vue.nextTick()
首先来了解一下JS的运行机制。
JS运行机制(Event Loop)
JS执行是单线程的,它是基于事件循环的。
- 所有同步任务都在主线程上执行,形成一个执行栈。
- 主线程之外,会存在一个任务队列,只要异步任务有了结果,就在任务队列中放置一个事件。
- 当执行栈中的所有同步任务执行完后,就会读取任务队列。那些对应的异步任务,会结束等待状态,进入执行栈。
- 主线程不断重复第三步。
这里主线程的执行过程就是一个tick,而所有的异步结果都是通过任务队列来调度。Event Loop 分为宏任务和微任务,无论是执行宏任务还是微任务,完成后都会进入到一下tick,并在两个tick之间进行UI渲染。
由于Vue DOM更新是异步执行的,即修改数据时,视图不会立即更新,而是会监听数据变化,并缓存在同一事件循环中,等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。为了确保得到更新后的DOM,所以设置了 Vue.nextTick()方法。
2、什么是Vue.nextTick()
是Vue的核心方法之一,官方文档解释如下:
在下次DOM更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM。
3、如何使用
语法: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被多次触发,只会被推入到队列中一次。