Vue.js 实现了一个 nextTick 函数,传入一个 cb ,这个 cb 会被存储到一个队列中,在下一个 tick 时触发队列中的所有 cb 事件。
Vue源码中根据不同平台,使用不同的方式开启异步队列。具体实现可查看 next-tick.js
- 支持Promise,使用Promise.resolve().then(flushCallbacks)
- PhantomJS and iOS 7.x 使用MutationObserver
- Internet Explorer 10 使用 setImmediate
- 其他 使用 setTimeout
输入
let watch1 = new Watcher();
let watch2 = new Watcher();
watch1.update();
watch1.update();
watch2.update();
---------------------------------------------
输出(不采用批量异步更新)
watch1 update
watch1视图更新啦~
watch1 update
watch1视图更新啦~
watch2 update
watch2视图更新啦~
----------------------------------------------
(采用批量异步更新)
watch1 update
watch1 update
watch2 update
watch1视图更新啦~
watch2视图更新啦~
复制代码
let uid = 0;
class Watcher {
constructor(){
this.id = ++uid;
}
update() {
console.log('watch' + this.id + ' update');
//把需要更新的渲染watcher放入队列
queueWatcher(this);
}
run(){
console.log('watch' + this.id + ' 视图更新了');
}
}
let callbacks = [];
let queue = [];
let pending = false;
let waiting = false;
let timerFunc;
let has = {};
function queueWatcher(watcher) {
//这要打一个断点,不然看不到push watcher2的过程
debugger;
let id = watcher.id;
if (has[id] == null) {
has[id] = true;
queue.push(watcher);
if (!waiting) {
waiting = true;
/*开启一个异步队列*/
nextTick(flushSchedulerQueue);
}
}
}
timerFunc=()=>{
/*主线程执行完后,执行flushCallbacks函数*/
Promise.resolve().then(flushCallbacks);
}
function nextTick(cb) {
callbacks.push(cb);
if (!pending) {
pending=true
timerFunc();
}
}
/*执行flushSchedulerQueue*/
function flushCallbacks() {
pending = false;
const copies = callbacks.slice(0);
callbacks.length = 0;
for (let i = 0; i < copies.length; i++) {
copies[i]();
}
}
/*批量执行watcher.run*/
function flushSchedulerQueue() {
let watcher, id;
for (let i = 0; i < queue.length; i++) {
watcher = queue[i];
id = watcher.id;
has[id] = null;
watcher.run();
}
waiting = false;
}
(function () {
debugger;
let watch1 = new Watcher();
let watch2 = new Watcher();
watch1.update();
watch1.update();
watch2.update();
})();
复制代码