使用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
將開啓一個隊列,緩衝在同一事件循環中發生的所有數據變更,所有操作執行完成後再更新DOM。
在下一個事件循環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;
})
}
}