我们都知道vue的数据频繁变化,但dom只会更新一次,其实现原理是将多次更新保存到一个队列中,然后一次性更新,避免重复更新相同的dom。
但具体是如何实现的并不清楚。
一开始以为是通过防抖节流的方式实现,但明显存在很大问题,经查阅发现是通过nextTick利用浏览器事件循环实现的队列缓存、批量更新。
nextTick
vue nextTick优先使用Promise微任务实现,优先级:Promise > MutationObserver > setImmediate > setTimeout
浏览器事件循环
同时需要知道,一个事件循环中,先执行宏任务代码,宏任务中会产生Promise等微任务,微任务是在当前宏任务结束前才执行,所以当前宏任务中产生多个update数据更新,等到微任务时才会真正执行,同时时效性也非常高。
代码Demo
let queue = [];
let has = {};
let pending = false;
class Watch {
update(obj) {
queueWatcher(this, obj);
}
run() {
console.log("run");
}
}
function queueWatcher(watcher, obj) {
const id = obj.id;
if (!has[id]) {
// push update队列
queue.push(watcher);
has[id] = true;
if (!pending) {
pending = true;
// 生成微任务
nextTick(flushSchedulerQueue);
}
}
}
function flushSchedulerQueue() {
queue.forEach((watcher) => {
watcher.run();
});
queue = [];
has = {};
pending = false;
}
function nextTick(flushCallbacks) {
return Promise.resolve().then(flushCallbacks);
}
var watch1 = new Watch();
// 宏任务代码,会触发多次update,产生一个微任务
watch1.update({id: 1});
watch1.update({id: 1});// 2次id相同只会执行一次
watch1.update({id: 2});// id不同正常执行
// log: run run