Vue.nextTick()定义:在下次dom更新循环结束之后,执行延迟回调
所以从定义上理解,它本身是一个异步更新队列,下面来看一下它的源码
function renderMixin (Vue) { // install runtime convenience helpers installRenderHelpers(Vue.prototype); Vue.prototype.$nextTick = function (fn) { return nextTick(fn, this) }; Vue.prototype._render = function () { var vm = this; var ref = vm.$options; var render = ref.render; var _parentVnode = ref._parentVnode; if (_parentVnode) { vm.$scopedSlots = normalizeScopedSlots( _parentVnode.data.scopedSlots, vm.$slots, vm.$scopedSlots ); } // set parent vnode. this allows render functions to have access // to the data on the placeholder node. vm.$vnode = _parentVnode; // render self var vnode; try { // There's no need to maintain a stack becaues all render fns are called // separately from one another. Nested component's render fns are called // when parent component is patched. currentRenderingInstance = vm; vnode = render.call(vm._renderProxy, vm.$createElement); } catch (e) { handleError(e, vm, "render"); // return error render result, // or previous vnode to prevent render error causing blank component /* istanbul ignore else */ if (vm.$options.renderError) { try { vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e); } catch (e) { handleError(e, vm, "renderError"); vnode = vm._vnode; } } else { vnode = vm._vnode; } } finally { currentRenderingInstance = null; } // if the returned array contains only a single node, allow it if (Array.isArray(vnode) && vnode.length === 1) { vnode = vnode[0]; } // return empty vnode in case the render function errored out if (!(vnode instanceof VNode)) { if (Array.isArray(vnode)) { warn( 'Multiple root nodes returned from render function. Render function ' + 'should return a single root node.', vm ); } vnode = createEmptyVNode(); } // set parent vnode.parent = _parentVnode; return vnode }; }
这段代码其实要看的就是return nextTick(fn, this)这个函数, 它放到Mixin很是巧妙,每执行一个都可以声明到执行栈排到堆里面再销毁,互不影响。
下面再看nextTick函数
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(); } // $flow-disable-line if (!cb && typeof Promise !== 'undefined') { return new Promise(function (resolve) { _resolve = resolve; }) } }
if (typeof Promise !== 'undefined' && isNative(Promise)) { var p = Promise.resolve(); timerFunc = function () { 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.x MutationObserver.toString() === '[object MutationObserverConstructor]' )) { // Use MutationObserver where native Promise is not available, // e.g. PhantomJS, iOS7, Android 4.4 // (#6466 MutationObserver is unreliable in IE11) 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)) { // Fallback to setImmediate. // Techinically it leverages the (macro) task queue, // but it is still a better choice than setTimeout. timerFunc = function () { setImmediate(flushCallbacks); }; } else { // Fallback to setTimeout. timerFunc = function () { setTimeout(flushCallbacks, 0); }; }
这里有几个判断,
1、支持Promise的时候那么就用Promise.then的方式来延迟函数调用,Promise通过pending判断 将函数延迟到当前函数调用栈最末端,也就是函数调用栈最后调用该函数/
2、这里还有一个关键字**MutationObserver ,**MutationObserver是h5新加的一个功能,其功能是监听dom节点的变动,在所有dom变动完成后,执行回调函数。
具体有一下几点变动的监听:
childList:子元素的变动
attributes:属性的变动
characterData:节点内容或节点文本的变动
subtree:所有下属节点(包括子节点和子节点的子节点)的变动
3,timerFunc函数 所以很明显,当判断出浏览器不支持上面两周方法的时候会通setTimeout延时更新
总结
在调用$nextTick函数的时候,Vue首先 会根据当前浏览器环境优先使用原生的 Promise.then 和 MutationObserver,如果都不支持,就会采用 setTimeout 代替,目的是 延迟函数到 DOM 更新后再使用
腾讯鹅厂首面提到的,结和宏任务,$nextTick遇到promise状况,微任务执行提到的。有兴趣的key看下对应的宏任务微任务介绍_juejin.cn/post/684490… _
promise详解_juejin.cn/post/685457…_
WEBGL探索之路 (二)--webgl场景构建 WEBGL探索之路 (一)--认识webgl js循环中如果操作了原数组会发生什么? 字节头条面试踩坑arguments js函数柯里化 强缓存与协商缓存属性定义集合 React Context 的理解以及应用 react shoudcomponentupdate 停止渲染 vue为什么不需要 深拷贝浅拷贝的十一种方法 import和require的区别比较 vue Vue $nextTick 深度解析 深度揭秘 Promise 微任务注册和执行过程 v8引擎如何执行一段js代码的? http3.0 2.0 1.0区别比较 宏观任务与微观任务详解