/** * Defer a task to execute it asynchronously. Ideally this * should be executed as a microtask, so we leverage * MutationObserver if it's available, and fallback to * setTimeout(0). * * @param {Function} cb * @param {Object} ctx */
exportconst nextTick = (function () { var callbacks = []; var pending = false; var timerFunc; functionnextTickHandler() { pending = false; var copies = callbacks.slice(0); callbacks = []; for (var i = 0; i < copies.length; i++) { copies[i](); } }
/* istanbul ignore if */ if (typeofMutationObserver !== 'undefined' && !hasMutationObserverBug) { var counter = 1; var observer = newMutationObserver(nextTickHandler); var textNode = document.createTextNode(counter); observer.observe(textNode, { characterData: true, }); timerFunc = function () { counter = (counter + 1) % 2; textNode.data = counter; }; } else { // webpack attempts to inject a shim for setImmediate // if it is used as a global, so we have to work around that to // avoid bundling unnecessary code. const context = inBrowser ? window : typeofglobal !== 'undefined' ? global : {}; timerFunc = context.setImmediate || setTimeout; } returnfunction (cb, ctx) { var func = ctx ? function () { cb.call(ctx); } : cb; callbacks.push(func); if (pending) return; pending = true; timerFunc(nextTickHandler, 0); }; })();
var callbacks = []; var pending = false; var timerFunc; functionnextTickHandler() { pending = false; var copies = callbacks.slice(0); callbacks = []; for (var i = 0; i < copies.length; i++) { copies[i](); } }
var counter = 1; var observer = newMutationObserver(nextTickHandler); var textNode = document.createTextNode(counter); observer.observe(textNode, { characterData: true, }); timerFunc = function () { counter = (counter + 1) % 2; textNode.data = counter; };
const nextTick = function (cb, ctx) { // 如果ctx参数存在,则为回调函数绑定this var func = ctx ? function () { cb.call(ctx); } : cb; callbacks.push(func); if (pending) return; pending = true; timerFunc(nextTickHandler, 0); };
核心的部分为:
1 2 3 4 5 6 7 8 9 10
var counter = 1; var observer = newMutationObserver(nextTickHandler); var textNode = document.createTextNode(counter); observer.observe(textNode, { characterData: true, }); timerFunc = function () { counter = (counter + 1) % 2; textNode.data = counter; };
在调用observe时,传入的参数有:
因为 Mutation Observer 则是异步触发,DOM 发生变动以后,并不会马上触发,而是要等到当前所有 DOM 操作都结束后才触发。 调用 timerFunc 时,因为 DOM 操作已经结束,此刻触发注册的回调,就能获取到更新后的回调。
队列更新
在看文档时,也有注意这句话:
Vue.js 默认异步更新 DOM。每当观察到数据变化时,Vue 就开始一个队列,将同一事件循环内所有的数据变化缓存起来。如果一个 watcher 被多次触发,只会推入一次到队列中。等到下一次事件循环,Vue 将清空队列,只进行必要的 DOM 更新。
callback <Function> The functiontocall at the endof this turn of the Node.js EventLoop [, ...arg] Optional arguments to pass when the callback is called.