Vue全局 API 之 nextTick 函数的实现

Vue 官方文档中对于 `nextTick` 函数的介绍为 : **在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。**

那么这句话是什么意思呢 ?

我们通过如下的一段代码来说明

```html

```

```javascript

  mounted() {

    this.msg = '修改数据的页面';

    this.$nextTick().then(() => {

      console.warn('当前的数据已经更新'); // console 2

    });

    console.warn('当前的数据还未更新'); // console 1

  }

```

我们在两个 `console.warn()` 处打断点,最后结果为先执行 `console 1` ,执行到该处,页面的数据未被替换为 **修改数据的页面** ,而当执行到 `console  2` 的时候,页面数据才会被修改,即DOM 更新结束。

------

接下来我们看一看 Vue 源码中对于 `nextTick()` 函数的实现

在 Vue 的入口文件 `src/core/index.js` 中我们可以看到如下初始化 全局API 的代码

```javascript

import { initGlobalAPI } from './global-api/index';

initGlobalAPI(Vue);

```

在 `initGlobalAPI` 方法中,直接将 `nextTick` 设置到了Vue上

```javascript

import { nextTick } from '../util/index';

export function initGlobalAPI(Vue: GlobalAPI) {

  Vue.set = set;

  Vue.delete = del;

  Vue.nextTick = nextTick;

}

```

这里我们找到了 `nextTick()` ,函数的定义了,其代码如下

```javascript

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

  let _resolve;

  callbacks.push(() => {

    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') { // 支持 Promise

    return new Promise(resolve => {

      _resolve = resolve;

    });

  }

}

```

在上面的代码中,先将一个函数添加到 `callbacks` 数组中,然后判断 `pending` 的值如果为 `false`,则执行将其值设置为 `true`,然后执行 `timerFunc()` 方法

我们考虑看一下在 支持 `Promise` 的环境中, `timerFunc()` 方法的定义

```javascript

if (typeof Promise !== 'undefined' && isNative(Promise)) { // 支持 Promise

  const p = Promise.resolve();

  timerFunc = () => {

    p.then(flushCallbacks); // Promise.then 方法将函数延迟到当前函数调用栈最末端,最后调用该函数。从而做到延迟。

    if (isIOS) setTimeout(noop); // noop => function noop(a?: any, b?: any, c?: any) {}

  };

  isUsingMicroTask = true;

}

```

可知在 `timerFunc()` 中使用 `Promise.then`进行延时,在当前函数调用栈全部执行完之后(DOM 更新结束),再执行  `flushCallbacks()` 函数。

`flushCallbacks` 函数定义如下

```javascript

function flushCallbacks() {

  pending = false;

  const copies = callbacks.slice(0); // 深拷贝 callbacks

  callbacks.length = 0; // 清空 callbacks

  for (let i = 0; i < copies.length; i++) { // 执行 callbacks 中的所有函数

    copies[i]();

  }

}

```

在最后,考虑浏览器支持 Promise 的情况下,我们将 Promise resloved 的回调赋值给`_resolve`。

联系最开始的例子,即 `_resolve` 的值为如下函数

```javascript

() => { console.warn('当前的数据已经更新'); }

```

综上所述,在考虑浏览器支持 Promise 的情况下,上面 `nextTick` 函数所作的事情如下

1. 将 `_resolve` 函数(`() => { console.warn('当前的数据已经更新'); }`)添加到 `callbacks` 队列中

2.  在 `pending` 为 `false` 的情况下, 用`Promise.then`延时保证DOM 更新结束,然后执行 `callbacks`队列中的所有函数

你可能感兴趣的:(Vue全局 API 之 nextTick 函数的实现)