前言
nextTick 的作用: 让回调函数在DOM更新完成后被调用。
1 | 举个栗子: |
原理
Vue的特点:响应式。但是数据更新时,DOM不会立即更新。因为Vue在更新DOM时是异步执行的,只要侦听到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watch被多次触发,只会被推入到队列中一次(去除重复数据,避免不必要的计算和DOM操作)。当刷新(执行)队列时,DOM会在下一个事件循环‘tick’中更新。
使用
1 | import Vue from 'vue' |
应用场景
- created()钩子函数进行的DOM操作一定要放在nextTick()的回调函数中
- 因为created() 时DOM还没有渲染,写在nextTick()中相当于mounted()函数,DOM的挂载和渲染已完成
- 如果某个操作,需要使用随数据改变的DOM结构时,需要放在nextTick()的回调函数中
源码浅析
源代码在@vue/src/core/util/next-tick.js
定义变量:
1 | var callbacks = []; // 存储回调函数的数组 |
$nextTick内实际调用的函数:(不直接操作callbacks,是为了防止后续有新的回调函数操作传入)
1 | function flushCallbacks () { |
接下来,分了4种情况来延迟调用flushCallbacks()(依次优雅降序)
1、Promise.then 延迟调用
1 | if (typeof Promise !== 'undefined' && isNative(Promise)) { |
Promise.then为microtasks的一种异步任务,当主线程(数据赋值)完成后,才会调用Promise.then,从而做到延迟(详情看microtask和task的文章)
2、MutationObserver 监听变化
1 | if (!isIE && typeof MutationObserver !== 'undefined' && ( |
MutationObserve是HTML5的一个新API,功能是:监听dom节点变化,在所有dom变动完成后,执行回调函数。
MO也是一个microtasks,在这里是创建一个文本节点,当主线程(数据赋值)完成后,会改变文本节点的内容来触发MutationObserve,执行回调,从而达到延迟的作用。
3、setImmediate 延迟器
1 | if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { |
4、setTimeout 延迟器
1 | { |
setImmediate和setTimeout都是task中的异步任务类型,它们会等待主线程(数据赋值)完成后,再起新的一个task执行回调函数,也起到了延迟的作用。setImmediate比setTimeout优先级更高。
闭包函数
1 | export function nextTick (cb?: Function, ctx?: Object) { |
每一次添加函数,都会向callbacks这个函数数组入栈。然后监听当前是否正在执行,如果没有,执行函数。
1 | this.$nextTick(function () { |