nextTick
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM
返回值
$nextTick()
方法返回一个 Promise
对象
当数据发生变化的时候,vue不会立刻更新dom,而是开启一个队列把更新函数放入队列中,然后刷新队列,这里是使用了promise,就是当同步任务完成后才刷新队列然后执行里面的更新函数。nextTick就是获取刚才的promise,然后用then方法执行nextTick的回调函数。就是在dom更新完成后才会调用nextTick的回调函数。
为什么 Vue 使用异步更新队列?
为了提升性能与效率。
每次数据变化都会触发 watcher
通知,从而重新渲染相关 dom
,异步方式可以将需要更新的 watcher
收集起来,如果重复,则不会被再次收集,
源码
callbacks
: 用来存储用户注册的回调函数(获得了更新后DOM
所进行的操作)pending
: 用来标记是否向任务队列添加任务,pending
为false
,表示任务队列没有nextTIck
任务,需要添加nextTick
任务,当添加一个nextTick
任务时,pending
为ture
,在回调执行之前还有nextTick
时,并不会重复添加任务到任务队列,当回调函数开始执行时,pending
为flase
,进行新的一轮事件循环。flushCallbacks
: 就是我们所说的被注册在任务队列中的任务,当这个函数执行,callbacks
中所有函数依次执行,然后清空callbacks
,并重置pending
为false
,所以说,一轮事件循环中,flushCallbacks
只会执行一次。microTimerFunc
: 它的作用就是使用Promise.then
将flushCallbacks
添加到微任务队列中。
JS
var timerFunc;
// 优先使用 promise 方法
if (typeof Promise !== 'undefined' && isNative(Promise)) {
var p = Promise.resolve();
timerFunc = function () {
p.then(flushCallbacks);
// ios 系统兼容
if (isIOS) { setTimeout(noop); }
};
isUsingMicroTask = true;
// 降级使用 MutationObserver 方法
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
// PhantomJS and iOS 7.x
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
var counter = 1;
var observer = new MutationObserver(flushCallbacks);
var textNode = document.createTextNode(String(counter));
observer.observe(textNode, {
characterData: true
});
timerFunc = function () {
counter = (counter + 1) % 2;
textNode.data = String(counter);
};
isUsingMicroTask = true;
// 以上方法都不存在时,降级使用 setImmediate,它也是微任务
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
timerFunc = function () {
setImmediate(flushCallbacks);
};
// 以上微任务方法都不存在,降级使用 setTimeout 宏任务
} else {
// Fallback to setTimeout.
timerFunc = function () {
setTimeout(flushCallbacks, 0);
};
}
function nextTick (cb, ctx) {
var _resolve;
callbacks.push(function () {
if (cb) {
try {
cb.call(ctx);
} catch (e) {
handleError(e, ctx, 'nextTick');
}
} else if (_resolve) {
_resolve(ctx);
}
});
if (!pending) {
pending = true;
timerFunc();
}
// $flow-disable-line
if (!cb && typeof Promise !== 'undefined') {
return new Promise(function (resolve) {
_resolve = resolve;
})
}
}
var timerFunc;
// 优先使用 promise 方法
if (typeof Promise !== 'undefined' && isNative(Promise)) {
var p = Promise.resolve();
timerFunc = function () {
p.then(flushCallbacks);
// ios 系统兼容
if (isIOS) { setTimeout(noop); }
};
isUsingMicroTask = true;
// 降级使用 MutationObserver 方法
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
// PhantomJS and iOS 7.x
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
var counter = 1;
var observer = new MutationObserver(flushCallbacks);
var textNode = document.createTextNode(String(counter));
observer.observe(textNode, {
characterData: true
});
timerFunc = function () {
counter = (counter + 1) % 2;
textNode.data = String(counter);
};
isUsingMicroTask = true;
// 以上方法都不存在时,降级使用 setImmediate,它也是微任务
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
timerFunc = function () {
setImmediate(flushCallbacks);
};
// 以上微任务方法都不存在,降级使用 setTimeout 宏任务
} else {
// Fallback to setTimeout.
timerFunc = function () {
setTimeout(flushCallbacks, 0);
};
}
function nextTick (cb, ctx) {
var _resolve;
callbacks.push(function () {
if (cb) {
try {
cb.call(ctx);
} catch (e) {
handleError(e, ctx, 'nextTick');
}
} else if (_resolve) {
_resolve(ctx);
}
});
if (!pending) {
pending = true;
timerFunc();
}
// $flow-disable-line
if (!cb && typeof Promise !== 'undefined') {
return new Promise(function (resolve) {
_resolve = resolve;
})
}
}