使用nextTick
全局使用:
// DOM 还没有更新
Vue.nextTick(function () {
// DOM 更新了
})
// 作为一个 Promise 使用
Vue.nextTick().then(function () {
// DOM 更新了
})
实例方法:
new Vue({
// ...
methods: {
// ...
example: function () {
// 修改数据
this.message = 'changed'
// DOM 还没有更新
this.$nextTick(function () {
// DOM 现在更新了
// `this` 绑定到当前实例
this.doSomethingElse()
})
}
}
})
//全局方法
Vue.nextTick = nextTick;
//实例方法
Vue.prototype.$nextTick = function (fn) {
return nextTick(fn, this)
};
nextTick方法:
Vue在更新DOM时是异步执行,只要监听到数据变化,Vue
将开启一个队列,缓冲在同一事件循环中发生的所有数据变更。
在下一个事件循环tick中,Vue刷新队列并执行实际工作。(Vue内部对异步队列尝试使用原生Promise.then , MutationObserver, setImmediate
。如果不支持,就采用setTimeout
)
为了在数据变化之后等待Vue完成更新DOM, 可以在数据变化之后立即使用Vue.nextTick(callback)
,这样回调函数将在DOM更新完成后被调用
返回一个Promise
对象,可以只用async/await
语法
var callbacks = [];
var pending = false;
//刷新回调队列,Vue刷新队列并执行实际工作
function flushCallbacks () {
pending = false;
var copies = callbacks.slice(0);
callbacks.length = 0;
for (var i = 0; i < copies.length; i++) {
copies[i](); //执行回调方法
}
}
//是否启用宏任务
var useMacroTask = false;
//在events的创建和更新阶段调用
function withMacroTask (fn) {
return fn._withTask || (fn._withTask = function () {
useMacroTask = true;
var res = fn.apply(null, arguments);
useMacroTask = false;
return res
})
}
//将开启一个队列,缓冲在同一事件循环中发生的所有数据变更。
//微任务队列和宏任务队列,各自缓存一份
var microTimerFunc;
var macroTimerFunc;
//判断是否支持setImmediate, 指定宏任务
if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
macroTimerFunc = function () {
setImmediate(flushCallbacks);
};
} else if (typeof MessageChannel !== 'undefined' && (
isNative(MessageChannel) ||
// PhantomJS
MessageChannel.toString() === '[object MessageChannelConstructor]'
)) {
var channel = new MessageChannel();
var port = channel.port2;
channel.port1.onmessage = flushCallbacks;
macroTimerFunc = function () {
port.postMessage(1);
};
} else {
/* istanbul ignore next */
macroTimerFunc = function () {
setTimeout(flushCallbacks, 0);
};
}
//判断是否支持Promise, 指定微任务
if (typeof Promise !== 'undefined' && isNative(Promise)) {
var p = Promise.resolve();
microTimerFunc = function () {
p.then(flushCallbacks);
//通过添加一个空定时器,强制刷新微任务队列
if (isIOS) { setTimeout(noop); }
};
} else {
// fallback to macro
microTimerFunc = macroTimerFunc;
}
//nextTick方法
function nextTick (cb, ctx) {
var _resolve;
//回调队列中插入cb
callbacks.push(function () {
if (cb) {
try {
cb.call(ctx); //传入指定的上下文
} catch (e) {
handleError(e, ctx, 'nextTick');
}
} else if (_resolve) {
_resolve(ctx);
}
});
if (!pending) {
pending = true;
//根据useMacroTask的值判断执行微任务队列还是宏任务队列
if (useMacroTask) {
macroTimerFunc();
} else {
microTimerFunc();
}
}
// $flow-disable-line
//如果没有指定回调,并且支持Promise,那么最终返回一个Promise对象
if (!cb && typeof Promise !== 'undefined') {
return new Promise(function (resolve) {
_resolve = resolve;
})
}
}