Vue中nextTick的使用
在vue中开发当中,我们有时需要拿到dom元素更新以后的信息,当我们给变量赋值以后立即去获取dom元素信息,通常是获取到的是更新以前的,在此需要借助nextTick这个api。官方示例如下
// 修改数据
vm.msg = 'Hello'
// 此处DOM 还没有更新
// 推荐
Vue.nextTick(function () {
// 此处 DOM 更新了
})
// 不推荐
Vue.nextTick().then(function () {
// 此处 DOM 更新了
})
此处推荐第一种写法,在nextTick原理上,不一定返回一个Promise,也有可能是通过MutationObserver、setImmediate或setTimeout实现。
Vue.nextTick()这个方法在事件循环当中,不一定是微任务或者宏任务,其原理上的Promise和MutationObserver属于微任务,而setImmediate和setTimeout属于宏任务。
Vue中nextTick的原理解析
当我们在Vue中使用nextTick()时,会先定义好callbacks数组(把需要在dom元素更新以后执行的内容放到callbacks队列当中,以供dom元素更新以后再执行),pending状态(判断是否是在等待状态,避免多次执行),flushCallbacks()函数(把存入callbacks中的内容全部执行一遍),timerFunc()函数(根据浏览器对Promise、MutationObserver、setImmediate和setTimeout的支持情况,定义为不同的内容),nextTick()函数(往callbacks中添加回调内容,并调用timerFunc执行对应回调)。关键源码如下所示:
var callbacks = [];
var pending = false;
var timerFunc;
function flushCallbacks () {
pending = false;
var copies = callbacks.slice(0);
callbacks.length = 0;
for (var i = 0; i < copies.length; i++) {
copies[i]();
}
}
if (typeof Promise !== 'undefined' && isNative(Promise)) {
var p = Promise.resolve();
timerFunc = function () {
p.then(flushCallbacks);
if (isIOS) { setTimeout(noop); }
};
isUsingMicroTask = true;
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
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;
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
timerFunc = function () {
setImmediate(flushCallbacks);
};
} else {
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();
}
if (!cb && typeof Promise !== 'undefined') {
return new Promise(function (resolve) {
_resolve = resolve;
})
}
}
执行步骤:
- 调用nextTick (cb, ctx)函数,向callbacks中添加对应的回调
- 判断当前pending是否是false,如果是的话,改为true,同时调用timerFunc()函数(该函数具体是Promise、MutationObserver、setImmediate、setTimeout中的哪种,看代码中对该浏览器是否支持该方式来判断,涉及到的isIOS、isIE、isNative等函数均在vue原理上已经封装好)
- 当cb不存在时,返回一个Promise函数
- 执行flushCallbacks(),执行添加到callbacks中的函数(此时dom已经更新)
方法推荐:
推荐做一个demo,把vue源码下载下来,在nextTick()中打上debugger,进行单步调试,查看源码运行过程。