数据更新的起点
set(newVal) {
dep.notify()
}
notify
// src/core/observer/dep.js
notify() {
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update() // 执行watcher的更新函数
}
}
update
// src/core/observer/watcher.js
if (this.lazy) {
this.dirty = true;
} else if (this.sync) {
this.run();
} else {
queueWatcher(this); // this是当前watcher实例
}
queueWatcher
// src/core/observer/scheduler.js
const queue = [];
function queueWatcher(watcher) {
if (!flushing) {
queue.push(watcher);
}
if (!waiting) {
waiting = true;
nextTick(flushSchedulerQueue); // flushSchedulerQueue是一个函数,如下
}
}
flushSchedulerQueue
// src/core/observer/scheduler.js
function flushSchedulerQueue() {
for (index = 0; index < quene.length; index++) {
watcher = quene[index];
watcher.run(); // 执行更新
}
}
从这里可以看出,flushSchedulerQueue
是真正执行更新的地方,watcher.run
中执行了组件更新函数
nextTick
// src/core/util/next-tick.js
const callbacks = [];
function nextTick(cb, ctx) {
// 没有直接push cb, 可以捕获异常
callbacks.push(() => {
if (cb) {
try {
cb.call(ctx);
} catch (e) {}
}
});
if (!pending) {
pending = true;
timerFunc();
}
}
timerFunc
// src/core/util/next-tick.js
let timerFunc;
if (typeof Promise !== "undefined" && isNative(Promise)) {
const p = Promise.resolve();
timerFunc = () => {
p.then(flushCallbacks);
};
} else if (
!isIE &&
typeof MutationObserver !== "undefined" &&
(isNative(MutationObserver) ||
MutationObserver.toString() === "[object MutationObserverConstructor]")
) {
let counter = 1;
const observer = new MutationObserver(flushCallbacks);
const textNode = document.createTextNode(String(counter));
observer.observe(textNode, {
characterData: true,
});
timerFunc = () => {
counter = (counter + 1) % 2;
textNode.data = String(counter);
};
} else if (typeof setImmediate !== "undefined" && isNative(setImmediate)) {
timerFunc = () => {
setImmediate(flushCallbacks);
};
} else {
timerFunc = () => {
setTimeout(flushCallbacks, 0);
};
}
- timerFunc 函数的优先级
- Promise(微任务)
- MutationObserver(微任务)
- setImmediate(微任务)
- setTimeout(宏任务)
flushCallbacks
function flushCallbacks() {
pending = false;
const copies = callbacks.slice(0);
callbacks.length = 0;
for (let i = 0; i < copies.length; i++) {
copies[i]();
}
}
案例分析
<div id="d1">{{foo}}</div>
<script>
new Vue({
el: "#app",
data: {
foo: "hey~",
},
mounted() {
this.foo = "haha";
this.$nextTick(() => {
console.log(d1.innerHTML);
});
},
});
</script>
this.foo
会使触发set更新函数- 经过notify->watcher的update->queueWatcher->next(flushSchedulerQueue)之后
- callbacks中自动存入了一个flushSchedulerQueue
callbacks = [flushSchedulerQueue];
- 执行$nextTick后,$nextTick传入的函数会直接添加到callbacks中
- 此时的callbacks
callbacks = [
flushSchedulerQueue,
() => {
console.log(d1.innerHTML);
},
];
callbacks
依次执行时- 先触发
flushSchedulerQueue
,其中执行了dom更新 - 再触发$nextTick的回调时,就可以拿到更新后的值
整体流程
- set中触发notify
- 执行watcher的update
- queneWatcher(watcher)
- nextTick(flushSchedulerQueue)
- callbacks.push(flushSchedulerQueue)
- timerFunc() // 放入异步队列
- 同步任务执行完后执行异步任务
- 遍历callbacks执行flushSchedulerQueue
- 执行watcher.run()
- run中执行了watcher.get()
- get()中执行了this.getter.call(vm, vm) // getter就是updateComponent
- vm._update(vm._render(), hydrating) 更新dom