「Vue 源码」nextTick 的执行逻辑
Defer the callback to be executed after the next DOM update cycle. Use it immediately after you’ve changed some data to wait for the DOM update. https://vuejs.org/v2/api/#Vue-nextTick
在下一次 DOM 更新结束后,执行的回调函数,更改了数据,但想等着 DOM 更新结束,那就可以使用这个方法。
Vue.prototype.$nextTick = function (fn) {return nextTick(fn, this)};
const callbacks = [];let pending = falsefunction flushCallbacks () {pending = falseconst copies = callbacks.slice(0);//浅拷贝数组callbacks.length = 0for (let i = 0; i < copies.length; i++) {copies[i]()}}
export function nextTick (cb?: Function, ctx?: Object) {let _resolvecallbacks.push(() => {if (cb) {try {cb.call(ctx)} catch (e) {handleError(e, ctx, 'nextTick')}} else if (_resolve) {_resolve(ctx)}})if (!pending) {pending = truetimerFunc()}}
单线程的 JS 为了防止出错影响到后面的执行,用 try catch 捕获了,就算传入的方法出错,也不会影响后面的执行。
if (!cb && typeof Promise !== 'undefined') {return new Promise(resolve => {_resolve = resolve})}
this.$nextTick(()=>{console.log('updata data');})// Promisethis.$nextTick().then(()=>{console.log('updata data');})
function isNative (Ctor) {return typeof Ctor === 'function' && /native code/.test(Ctor.toString())}
if (typeof Promise !== 'undefined' && isNative(Promise)) {const p = Promise.resolve()timerFunc = () => {p.then(flushCallbacks)// In problematic UIWebViews, Promise.then doesn't completely break, but// it can get stuck in a weird state where callbacks are pushed into the// microtask queue but the queue isn't being flushed, until the browser// needs to do some other work, e.g. handle a timer. Therefore we can// "force" the microtask queue to be flushed by adding an empty timer.if (isIOS) setTimeout(noop)}isUsingMicroTask = true} else if (!isIE && typeof MutationObserver !== 'undefined' && (isNative(MutationObserver) ||// PhantomJS and iOS 7.xMutationObserver.toString() === '[object MutationObserverConstructor]')) {// Use MutationObserver where native Promise is not available,// e.g. PhantomJS, iOS7, Android 4.4// (#6466 MutationObserver is unreliable in IE11)let counter = 1const observer = new MutationObserver(flushCallbacks)const textNode = document.createTextNode(String(counter))observer.observe(textNode, {characterData: true})timerFunc = () => {counter = (counter + 1) % 2textNode.data = String(counter)}isUsingMicroTask = true} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {// Fallback to setImmediate.// Techinically it leverages the (macro) task queue,// but it is still a better choice than setTimeout.timerFunc = () => {setImmediate(flushCallbacks)}} else {// Fallback to setTimeout.timerFunc = () => {setTimeout(flushCallbacks, 0)}}
nextTick 执行的是一套微任务队列,如果当前设备不支持 MutationObserver 或者 Promise ,那就会转为宏任务 setTimeout ,关于宏任务和微任务在队列中执行顺序的区别,可以参考之前文章:
https://github.com/vuejs/vue/blob/2.6/src/core/util/next-tick.js
推荐阅读
