一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情。
简介
版本:2.6.14
核心:异步执行回调
变量作用
- callbacks:用来保存 nextTick 的回调函数的数组
- pending:防止多次调用 timerFunc
- flushCallbacks:真正执行 callbacks 的函数,会将 callbacks 清空,重置 pending
- timerFunc:初始化成合适当前环境的异步函数,目标是触发 flushCallbacks
timerFunc 的封装
判断基本大同小于,这边只说基于不同情况下 timerFunc 的封装
-
Promise:promises.resolve().then 传入 flushCallbacks,使其进入微任务队列。还会根据是否 ios 的判断,使用 setTimeout 去执行一个空函数 noop
- 为什么要用触发 setTimeout 触发一个空函数?是因为在 ios 的 UIWebViews 环境下,回调推送到微任务队列后不会立即刷新,通过添加空定时器来强制刷新微任务队列
-
MutationObserver:传入 flushCallbacks 到 MutationObserver 实例,创建一个 text 元素并监听,timerFunc 被调用的时候,会修改 text 元素,然后会触发 flushCallbacks
-
setImmediate:直接 setImmediate(flushCallbacks)
-
setTimeout:直接 setTimeout(flushCallbacks, 0)
其中进入 Promise 和 MutationObserver 判断分支,还会标记 isUsingMicroTask 为 true,用来修复 dom 触发两次事件回调,具体可以看src/platforms/web/runtime/modules/events.js的 add 函数
nextTick
Vue.nextTick 和 Vue.prototype.$nextTic 具体差别是后者会多传一个 this
nextTick 函数如下
export function nextTick(cb?: Function, ctx?: Object) {
let _resolve;
// push进去callbacks
callbacks.push(() => {
if (cb) {
try {
cb.call(ctx);
} catch (e) {
handleError(e, ctx, "nextTick");
}
} else if (_resolve) {
_resolve(ctx);
}
});
// 防止多次调用timerFunc
if (!pending) {
pending = true;
timerFunc();
}
// 如果没有回调函数,并且支持Promise,那就返回Promise实例
if (!cb && typeof Promise !== "undefined") {
return new Promise((resolve) => {
_resolve = resolve;
});
}
}
整个执行顺序 nextTick->timerFunc->flushCallbacks
最终flushCallbacks会循环调用里面的回调函数,并把相关数据初始化