再来简单的写一下notify和nextTick这部分代码
nextTick的作用就是在每次notify的时候,让watcher的更新操作放到下一个tick,而在本次执行栈中,只是把watcher收集到一个数组里。
这样做的好处:因为watcher每次更新就会重新执行render(生成vnode)和update(生成真实dom),但是有时候一次会给很多data赋值,而多个data服务于同一个watcher的可能性是很大的,所以这种情况我们只用更新一次watcher即可。
从watcher的update方法开始,在没有做队列的时候我是这样写的
// watcher
update() {
this.get();
}
queue
那么我们想把同一个tick的watcher收集到一个队列里,那么就在这里改动,这里把 当前watcher收集进队列,用一个数组queue来存放watcher。这个queue收集当前watcher,在nextTick的回调中清空,因为nextTick就是下一个tick。
has
这里为了不收集相同依赖,所以需要一个容器来存放已经被收集过的watcher,这里我们用一个对象来存放已经收集过的watcher的id,watcher需要添加id,每次新增一个watch,id就加 1,这样一个可以保证watcher的id唯一,并且还可以知道watcher生成的先后顺序,先生成的是后面生成的父级。
waiting
waiting用来控制什么时候执行nextTick。waiting为true时不允许执行nextTick,false的时候可以执行nextTick。那么问题就变成如何控制这个waiting的值。
执行下一个tick之前,waiting是true,nextTick执行waiting变为false。
if (!waiting) {
waiting = true;
nextTick(() => {
waiting = false;
// ...
});
}
这里需要调整一下,nextTick的回调里需要做什么逻辑?
首先要知道事件队列这个概念
js引擎遇到一个异步事件后并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务。当一个异步事件返回结果后,js会将这个事件加入与当前执行栈不同的另一个队列,我们称之为事件队列。
也就是说,当我们频繁的在给data设值的时候,程序是不会执行nextTick回调的,程序会先把当前执行栈中的task执行完之后,再去存放异步的任务队列里找任务来执。
现在再来想一下nextTick回调中应该处理什么逻辑
首先执行一次执行收集的watcher的更新
然后,就是重置一些变量,重置的就是上面提到的waiting, has, queue
差不多长这样
let queue = [];
let has = {};
let waiting = false;
function resetSchedulerState() {
queue = [];
has = {};
waiting = false;
}
function flushSchedulerQueue () {
for (const watcher of queue) {
watcher.get();
}
resetSchedulerState();
}
export function queueWatcher (watcher) {
const id = watcher.id;
if (!has[id]) {
has[id] = true;
queue.push(watcher);
}
if (!waiting) {
waiting = true;
nextTick(flushSchedulerQueue);
}
}
接下来就是实现nextTick
nextTick就很简单
这里我不看MutationObserver(这货只有ie支持)和setImmediate(node里用的多),我简单的实现就是如果支持Promise的浏览器将就用Promise,不支持就用setTimeout
这里nextTick内部也做了waiting相同的处理
timerFunc
用于存放最后用到的nextTick的执行函数,有可能是微任务promise也有可能是宏任务setTimeout,这个在2.5以前做了区分,在2.5版本以后没有做区分了
callbacks
用于存放传入的cb
pending
等同于上面的waiting
这里nextTick内部其实也实现了的功能
长这样
let timerFunc;
let callbacks = [];
let pending = false;
function flushCallbacks () {
pending = false;
for (const cb of callbacks) {
cb();
}
pending = false;
callbacks = [];
}
if (typeof Promise !== 'undefined') {
const p = Promise.resolve();
timerFunc = () => {
p.then(flushCallbacks);
};
} else {
timerFunc = () => {
setTimeout(flushCallbacks);
};
}
export function nextTick (cb) {
callbacks.push(cb);
if (!pending) {
pending = true;
timerFunc();
}
}
网友评论