- 因为Vue的异步更新队列,$nextTick是用来知道什么时候DOM更新完成的。
- Vue在观察到数据变化时并不是直接更新DOM,而是开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。
实现原理
- 先判断是否原生支持promise,如果支持,则利用promise来触发执行回调函数;
- 否则如果支持MutationObserver,则实例化一个观察者对象,观察文本节点发生变化时,触发执行所有回调函数。
- 如果都不支持,则利用setTimeout设置延时为0。
- 每次event loop的最后,会有一个UI render,也就是更新DOM
- 只要让nextTick里的代码放在UI render步骤后面执行,就能访问到更新后的DOM了。
https://blog.csdn.net/qq_38290251/article/details/107550899
var nextTick = (function () {var callbacks = []; // 存储需要触发的回调函数var pending = false; // 是否正在等待的标识(false:允许触发在下次事件循环触发callbacks中的回调, true: 已经触发过,需要等到下次事件循环)var timerFunc; // 设置在下次事件循环触发callbacks的 触发函数//处理callbacks的函数function nextTickHandler () {pending = false;// 可以触发timeFuncvar copies = callbacks.slice(0);//复制callbackcallbacks.length = 0;//清空callbackfor (var i = 0; i < copies.length; i++) {copies[i]();//触发callback回调函数}}//如果支持Promise,使用Promise实现if (typeof Promise !== 'undefined' && isNative(Promise)) {var p = Promise.resolve();var logError = function (err) { console.error(err); };timerFunc = function () {p.then(nextTickHandler).catch(logError);// ios的webview下,需要强制刷新队列,执行上面的回调函数if (isIOS) { setTimeout(noop); }};//如果Promise不支持,但是支持MutationObserver(h5新特性,异步,当dom变动是触发,注意是所有的dom都改变结束后触发)} else if (typeof MutationObserver !== 'undefined' && (isNative(MutationObserver) ||// PhantomJS and iOS 7.xMutationObserver.toString() === '[object MutationObserverConstructor]')) {// use MutationObserver where native Promise is not available,// e.g. PhantomJS IE11, iOS7, Android 4.4var counter = 1;var observer = new MutationObserver(nextTickHandler);//创建一个textnode dom节点,并让MutationObserver 监视这个节点;而 timeFunc正是改变这个dom节点的触发函数var textNode = document.createTextNode(String(counter));observer.observe(textNode, {characterData: true});timerFunc = function () {counter = (counter + 1) % 2;textNode.data = String(counter);};} else {// 上面两种不支持的话,就使用setTimeouttimerFunc = function () {setTimeout(nextTickHandler, 0);};}//nextTick接受的函数, 参数1:回调函数 参数2:回调函数的执行上下文return function queueNextTick (cb, ctx) {var _resolve;//用于接受触发 promise.then中回调的函数//向回调数据中pushcallbackcallbacks.push(function () {//如果有回调函数,执行回调函数if (cb) { cb.call(ctx); }if (_resolve) { _resolve(ctx); }//触发promise的then回调});if (!pending) {//是否执行刷新callback队列pending = true;timerFunc();}//如果没有传递回调函数,并且当前浏览器支持promise,使用promise实现if (!cb && typeof Promise !== 'undefined') {return new Promise(function (resolve) {_resolve = resolve;})}}})();mounted: function () {this.message = "abc"this.$nextTick(function () {console.log(this.message)})}
