Vue 源码
你想要的 vue 源码分析 · 16
当数据发生变化的时候,触发 setter 逻辑,把在依赖过程中订阅的所有观察者,也就是 watcher 都触发他们的 update 过程,这个过程又利用了队列做了进一步的优化,在 nextTick 后执行所有 watcher 的 run,最后执行他们的回调函数 代码块 defineReactive dep.notify() subs.update(); queueWatcher flushSche…
当数据发生变化的时候,触发 setter 逻辑,把在依赖过程中订阅的所有观察者,也就是 watcher 都触发他们的 update 过程,这个过程又利用了队列做了进一步的优化,在 nextTick 后执行所有 watcher 的 run,最后执行他们的回调函数 代码块 defineReactive dep.notify() subs.update(); queueWatcher flushSche…
当数据发生变化的时候,触发 setter 逻辑,把在依赖过程中订阅的所有观察者,也就是 watcher 都触发他们的 update 过程,这个过程又利用了队列做了进一步的优化,在 nextTick 后执行所有 watcher 的 run,最后执行他们的回调函数
// src/core/observer/index.js
export function defineReactive(obj: Object, key: string, val: any, customSetter?: ?Function, shallow?: boolean) {
const dep = new Dep();
const property = Object.getOwnPropertyDescriptor(obj, key);
if (property && property.configurable === false) {
return;
}
// cater for pre-defined getter/setters
const getter = property && property.get;
const setter = property && property.set;
if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
}
let childOb = !shallow && observe(val);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
// ...
set: function reactiveSetter(newVal) {
const value = getter ? getter.call(obj) : val;
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return;
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter();
}
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
// shallow为false的时候,会把新设置的值编程一个响应式对象
childOb = !shallow && observe(newVal);
// 通知所有订阅者
dep.notify();
}
});
}
// src/core/observer/dep.js
class Dep {
// ...
notify() {
const subs = this.subs.slice();
// 遍历所有subs,subs就是Watcher实例数组
for (let i = 0, l = subs.length; i < l; i++) {
// 然后调用每一个watcher的update方法
subs[i].update();
}
}
}
// src/core/observer/watcher.js
class Watcher {
// ...
update() {
// 针对watcher状态,会执行不同的逻辑
// 针对计算属性
if (this.computed) {
// A computed property watcher has two modes: lazy and activated.
// It initializes as lazy by default, and only becomes activated when
// it is depended on by at least one subscriber, which is typically
// another computed property or a component's render function.
if (this.dep.subs.length === 0) {
// In lazy mode, we don't want to perform computations until necessary,
// so we simply mark the watcher as dirty. The actual computation is
// performed just-in-time in this.evaluate() when the computed property
// is accessed.
this.dirty = true;
} else {
// In activated mode, we want to proactively perform the computation
// but only notify our subscribers when the value has indeed changed.
this.getAndInvoke(() => {
this.dep.notify();
});
}
// 针对同步watcher的时候
} else if (this.sync) {
this.run();
} else {
// 一般组件更新的场景,都会走到这个逻辑
queueWatcher(this);
}
}
}
// src/core/observer/scheduler.js
const queue: Array<Watcher> = [];
let has: { [key: number]: ?true } = {};
let waiting = false;
let flushing = false;
// Vue在做派发更新的时候会做一个优化,并不会每次数据改变的时候都触发watcher的回调,
// 而是把这些watcher先添加到一个队列中,然后在nextTick后执行flushSchedulerQueue
export function queueWatcher(watcher: Watcher) {
const id = watcher.id;
// 首先用has对象保证同一个watcher只添加一次
if (has[id] == null) {
has[id] = true;
// 接着对flushing进行判断
if (!flushing) {
queue.push(watcher);
} else {
let i = queue.length - 1;
// 找到第一个待插入的watcher的id比当前队列中的watcher的id大的位置
while (i > index && queue[i].id > watcher.id) {
i--;
}
// 把watcher按照id插入队列,因为queue的长度发生变化
queue.splice(i + 1, 0, watcher);
}
// 通过wating保证nextTick(flushSchedulerQueue)只调用一次
if (!waiting) {
waiting = true;
// 执行完毕后flushing为true,继续执行queueWatcher
nextTick(flushSchedulerQueue);
}
}
}
三个过程:1、队列排序,2、队列遍历 3、状态恢复
// src/core/observer/scheduler.js
let flushing = false;
let index = 0;
// 执行完毕后,flushing为true
function flushSchedulerQueue() {
flushing = true;
let watcher, id;
// 队列排序
// 先对队列做了从小到大的排序
// 1.组件更新由父到子,父组件创建过程是优先于子组件的,所以watcher的创建也是先父后子,执行顺序也是先父后子
// 2.用户自定义的watcher要优先于渲染watcher执行,因为用户自定义watcher是在渲染watcher之前创建的
// 3.如果一个组件在父组件的watcher执行期间被销毁,那么它对应的watcher执行都可以被跳过,所以父组件的watcher先执行
queue.sort((a, b) => a.id - b.id);
// 队列遍历
// 每次遍历的时候都要重新计算queue的长度,因为很可能在run的过程中再次添加新的watcher
for (index = 0; index < queue.length; index++) {
watcher = queue[index];
if (watcher.before) {
watcher.before();
}
id = watcher.id;
has[id] = null;
// 因为很可能在run的过程中再次添加新的watcher,添加新的watcher就会再次执行queueWatcher
watcher.run();
// 判断是否无限循环更新,watcher.run()可能执行queueWatcher
if (process.env.NODE_ENV !== 'production' && has[id] != null) {
circular[id] = (circular[id] || 0) + 1;
if (circular[id] > MAX_UPDATE_COUNT) {
warn('You may have an infinite update loop ' + (watcher.user ? `in watcher with expression "${watcher.expression}"` : `in a component render function.`), watcher.vm);
break;
}
}
}
// keep copies of post queues before resetting state
const activatedQueue = activatedChildren.slice();
const updatedQueue = queue.slice();
// 状态恢复
resetSchedulerState();
// call component updated and activated hooks
callActivatedHooks(activatedQueue);
callUpdatedHooks(updatedQueue);
// devtool hook
/* istanbul ignore if */
if (devtools && config.devtools) {
devtools.emit('flush');
}
}
const queue: Array<Watcher> = [];
let has: { [key: number]: ?true } = {};
let circular: { [key: number]: number } = {};
let waiting = false;
let flushing = false;
let index = 0;
// 把这些控制流程状态的一些变量恢复到初始值,把watcher队列清空
function resetSchedulerState() {
index = queue.length = activatedChildren.length = 0;
has = {};
if (process.env.NODE_ENV !== 'production') {
circular = {};
}
waiting = flushing = false;
}
class Watcher {
// run函数实际上是执行this.getAndInvoke(this.cb)方法,并传入watcher回调函数
run() {
if (this.active) {
this.getAndInvoke(this.cb);
}
}
getAndInvoke(cb: Function) {
// 执行this.get()方法求值
// 对于渲染watcher来说,执行this.get()方法求值的时候,会执行getter方法
// updateComponent = () => {
// vm._update(vm._render(), hydrating);
// };
// 这就是当我们去修改组件的响应式数据的时候,就会触发组件的重新渲染的原因,然后就执行patch过程
const value = this.get();
if (
// 如果新旧值不等或者新值是对象或是deep模式
value !== this.value ||
// Deep watchers and watchers on Object/Arrays should fire even
// when the value is the same, because the value may
// have mutated.
isObject(value) ||
this.deep
) {
// 会执行watcher的回调,
const oldValue = this.value;
this.value = value;
this.dirty = false;
if (this.user) {
try {
// 回调执行的时候会把第一个和第二个参数传入新value,和旧值oldValue
// 这就是为什么当添加自定义watcher的时候能在回调函数的参数中拿到新旧值的原因
cb.call(this.vm, value, oldValue);
} catch (e) {
handleError(e, this.vm, `callback for watcher "${this.expression}"`);
}
} else {
cb.call(this.vm, value, oldValue);
}
}
}
}
暂无评论
确认操作
请再次确认编辑图片
待上传图片 (剩余 0 张)
项目详情
确认评论邮箱
邀请制评论