浅析 Vue 2.6 中的 nextTick 要领

浅析 Vue 2.6 中的 nextTick 要领。

事宜轮回

JS 的 事宜轮回使命行列 实际上是明白 nextTick 观点的症结。
这个网上实在有许多优良的文章做了细致引见,我就简朴过过了。

以下内容适用于浏览器端 JS,NodeJS 的事宜轮回机制并不相同。

范例中划定 task 分为两大类: task(macrotask)microtask

一般认为是 task 的使命源:

setTimeout / setInterval
setImmediate
MessageChannel
I/O
UI rendering

一般认为是 microtask 的使命源:

Promise
process.nextTick
MutationObserver
Object.observe(已烧毁)

简朴概略:(这里是官方范例)

  1. 起首最先实行 script 剧本,直到实行上下文栈为空时,最先清空 microtask 行列里的使命,行列嘛,先入先出,出一个实行一个,清空终了,走事宜轮回。
  2. 事宜轮回:不断地去取 task 行列的中的一个使命推入栈中实行,并在当次轮回里顺次清空 microtask 行列里的使命,清空以后,能够会触发页面更新衬着(由浏览器决议)。
  3. 以后反复 事宜轮回 步骤。

nextTick

Vue 中数据的变化到 DOM 的更新衬着是一个异步历程。
此要领便用于在 DOM 更新轮回完毕以后实行耽误回调。
运用要领很简朴:

// 修正数据
vm.msg = 'Hello';
// DOM 还没有更新
Vue.nextTick(function() {
  // DOM 更新了
});

// 作为一个 Promise 运用
Vue.nextTick().then(function() {
  // DOM 更新了
});

源码 去除解释,实在也只要不到一百来行,团体照样很轻易明白的。

这里划成 3 个部份引见。

模块变量

引见 引入的模块 和 定义的变量。

// noop 空函数,可用作函数占位符
import { noop } from 'shared/util';

// Vue 内部的错误处置惩罚函数
import { handleError } from './error';

// 推断是IE/IOS/内置函数
import { isIE, isIOS, isNative } from './env';

// 运用 MicroTask 的标识符
export let isUsingMicroTask = false;

// 以数组情势存储实行的函数
const callbacks = [];

// nextTick 实行状况
let pending = false;

// 遍历函数数组实行每一项函数
function flushCallbacks() {
  pending = false;
  const copies = callbacks.slice(0);
  callbacks.length = 0;
  for (let i = 0; i < copies.length; i++) {
    copies[i]();
  }
}

异步耽误函数

接下来是中心的 异步耽误函数。这里差别的 Vue 版本采纳的战略实在并不相同。

2.6 版本优先运用 microtask 作为异步耽误包装器。

2.5 版本则是 macrotask 连系 microtask。然则,在重绘之前状况转变时会有小题目(如 #6813)。另外,在事宜处置惩罚递次中运用 macrotask 会致使一些没法躲避的新鲜行动(如#7109#7153#7546#7834#8109)。

所以 2.6 版本如今又改用 microtask 了,为何是又呢。。由于 2.4 版本及之前也是用的 microtask。。。

microtask 在某些情况下也是会有题目的,由于 microtask 优先级比较高,事宜会在递次事宜(如#4521#6690 有变通要领)之间甚至在一致事宜的冒泡历程当中触发(#6566)。

// 中心的异步耽误函数,用于异步耽误挪用 flushCallbacks 函数
let timerFunc;

// timerFunc 优先运用原生 Promise
// 底本 MutationObserver 支撑更广,但在 iOS >= 9.3.3 的 UIWebView 中,触摸事宜处置惩罚递次中触发会发生严重错误
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  const p = Promise.resolve();
  timerFunc = () => {
    p.then(flushCallbacks);

    // IOS 的 UIWebView,Promise.then 回调被推入 microtask 行列然则行列能够不会准期实行。
    // 因而,增加一个空计时器“强迫”实行 microtask 行列。
    if (isIOS) setTimeout(noop);
  };
  isUsingMicroTask = true;

  // 当原生 Promise 不可用时,timerFunc 运用原生 MutationObserver
  // 如 PhantomJS,iOS7,Android 4.4
  // issue #6466 MutationObserver 在 IE11 并不牢靠,所以这里排除了 IE
} else if (
  !isIE &&
  typeof MutationObserver !== 'undefined' &&
  (isNative(MutationObserver) ||
    // PhantomJS 和 iOS 7.x
    MutationObserver.toString() === '[object MutationObserverConstructor]')
) {
  let counter = 1;
  const observer = new MutationObserver(flushCallbacks);
  const textNode = document.createTextNode(String(counter));
  observer.observe(textNode, {
    characterData: true,
  });
  timerFunc = () => {
    counter = (counter + 1) % 2;
    textNode.data = String(counter);
  };
  isUsingMicroTask = true;

  // 假如原生 setImmediate 可用,timerFunc 运用原生 setImmediate
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  timerFunc = () => {
    setImmediate(flushCallbacks);
  };
} else {
  // 末了的顽强,timerFunc 运用 setTimeout
  timerFunc = () => {
    setTimeout(flushCallbacks, 0);
  };
}

一句话总结优先级:microtask 优先
Promise > MutationObserver > setImmediate > setTimeout

nextTick 函数

nextTick 函数。接收两个参数:

  1. cb 回调函数:是要耽误实行的函数;
  2. ctx:指定 cb 回调函数 的 this 指向

Vue 实例要领 $nextTick 做了进一步封装,把 ctx 设置为当前 Vue 实例

export function nextTick(cb?: Function, ctx?: Object) {
  let _resolve;

  // cb 回调函数会经一致处置惩罚压入 callbacks 数组
  callbacks.push(() => {
    if (cb) {
      // 给 cb 回调函数实行加上了 try-catch 错误处置惩罚
      try {
        cb.call(ctx);
      } catch (e) {
        handleError(e, ctx, 'nextTick');
      }
    } else if (_resolve) {
      _resolve(ctx);
    }
  });

  // 实行异步耽误函数 timerFunc
  if (!pending) {
    pending = true;
    timerFunc();
  }

  // 当 nextTick 没有传入函数参数的时刻,返回一个 Promise 化的挪用
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve;
    });
  }
}

小结

团体看过来,觉得照样比较好明白的吧~ 2.6 版本比拟之前简化了一点。

小结一下,每次挪用 Vue.nextTick(cb) 会做些什么:
cb 函数经处置惩罚压入 callbacks 数组,实行 timerFunc 函数,耽误挪用 flushCallbacks 函数,遍历实行 callbacks 数组中的一切函数。

耽误挪用优先级以下:
Promise > MutationObserver > setImmediate > setTimeout

版本差别

实在 Vue 2.4、2.5、2.6 版本的 nextTick 战略都略不一样。

团体 2.62.4 的比较类似。(细致瞅了瞅,基础就是一样的,2.6 timerFunc 多了个 setImmediate 推断)

2.5 版本实在也差不多。。。源码写法有些不一样,团体优先级是:Promise > setImmediate > MessageChannel > setTimeout,假如更新是在 v-on 事宜处置惩罚递次中触发的,nextTick 会优先运用 macrotask。感兴趣可自行去末端的参考地点举行查阅。

参考:

API—Vue.js
事宜轮回范例
Vue nextTick v2.6.7 源码
Vue nextTick v2.5.22 源码
Vue nextTick v2.4.4 源码
从 event loop 范例探讨 javaScript 异步及浏览器更新衬着机遇

    原文作者:joy钰
    原文地址: https://segmentfault.com/a/1190000018328525
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞