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`队列中的所有函数