//观察者构造函数
var Observer = function Observer (value) {
this.value = value;
this.dep = new Dep();
//初始值为0;根数据对象的vmCount为1
this.vmCount = 0;
//订阅器和数据对象相互关联:订阅器为数据对象的_ob_属性,数据对象为订阅器的 value属性
def(value, '__ob__', this);
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods);
} else {
copyAugment(value, arrayMethods, arrayKeys);
}
this.observeArray(value);
} else {
//调用walk方法
this.walk(value);
}
};
上面源码中,首先只关注walk函数;
Observer.prototype.walk = function walk (obj) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
defineReactive$$1(obj, keys[i]);
}
};
执行walk函数时,此时函数调用链如下:
defineReactive$$1()函数会给对象中的每一个属性都定义一个监听;
function defineReactive$$1 (
obj,
key,
val,
customSetter,
shallow
) {
var dep = new Dep();
var property = Object.getOwnPropertyDescriptor(obj, key);
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
var getter = property && property.get;
var setter = property && property.set;
if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
}
var childOb = !shallow && observe(val);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
var value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend();
if (childOb) {
childOb.dep.depend();
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value
},
set: function reactiveSetter (newVal) {
var 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 (customSetter) {
customSetter();
}
// #7981: for accessor properties without setter
if (getter && !setter) {
return }
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
dep.notify();
}
});
defineReactive$$1()函数实现核心是内部调用了Object.defineProperty()函数;
get属性:
1 函数中的get方法Dep.target是数据属性关联的订阅者(Watcher);
2 Dep.target是targetStack栈中取出的当前值,初始时值为null;
Dep.target = null;
var targetStack = [];
function pushTarget (target) {
targetStack.push(target);
Dep.target = target;
}
function popTarget () {
targetStack.pop();
Dep.target = targetStack[targetStack.length - 1];
}
3 订阅者(Watcher)的get方法会往targetStack中push元素;(下面的订阅者源码会展示)
4 dep.depend()会把关联订阅者(Watcher)加入属性发布者dep的subs数组中;
set属性:
1 函数中的set方法会比较改变前后的新旧值;
2 如果值发生变化,会调用属性的发布者的notify通知关联订阅器(Watcher)进行视图更新;
小结:
1 实现观察的核心方法是 Object.defineProperty();
2 Object.defineProperty()中的get方法属性会把订阅者(Watcher)加入属性发布者(Dep)中;
3 Object.defineProperty()中的set方法,通知订阅器进行更新视图操作;
//发布者构造函数
var Dep = function Dep () {
//id为发布者的唯一标识
this.id = uid++;
//保存关联订阅者(Watcher)的数组
this.subs = [];
};
//添加订阅者(Watcher)
Dep.prototype.addSub = function addSub (sub) {
this.subs.push(sub);
};
//删除加订阅者(Watcher)
Dep.prototype.removeSub = function removeSub (sub) {
remove(this.subs, sub);
};
//和订阅者建立相互依赖关系
Dep.prototype.depend = function depend () {
if (Dep.target) {
Dep.target.addDep(this);
}
};
//通知订阅者更新
Dep.prototype.notify = function notify () {
// stabilize the subscriber list first
var subs = this.subs.slice();
if (!config.async) {
// subs aren't sorted in scheduler if not running async
// we need to sort them now to make sure they fire in correct
// order
subs.sort(function (a, b) {
return a.id - b.id; });
}
for (var i = 0, l = subs.length; i < l; i++) {
//调用关联的订阅者(Watcher)的更新函数
subs[i].update();
}
};
代码中的注释已经解释了构造函数和原型链中函数的作用
var Watcher = function Watcher (
vm,
expOrFn,//表达式或函数,如果为函数的话,就赋值为订阅者的getter方法
cb,
options,//计算属性的optiongs.lazy值为true
isRenderWatcher//计算属性的watcher这个参数不传值
) {
this.vm = vm;
if (isRenderWatcher) {
vm._watcher = this;
}
vm._watchers.push(this);
// options
if (options) {
this.deep = !!options.deep;
this.user = !!options.user;
this.lazy = !!options.lazy;
this.sync = !!options.sync;
this.before = options.before;
} else {
this.deep = this.user = this.lazy = this.sync = false;
}
this.cb = cb;
this.id = ++uid$2; // uid for batching
this.active = true;
this.dirty = this.lazy; // for lazy watchers
this.deps = [];
this.newDeps = [];
this.depIds = new _Set();
this.newDepIds = new _Set();
this.expression = expOrFn.toString();
// parse expression for getter
if (typeof expOrFn === 'function') {
this.getter = expOrFn;
} else {
this.getter = parsePath(expOrFn);
...
}
//计算订阅者的this.lazy为true,value为undefined
this.value = this.lazy
? undefined
: this.get();
};
关注上面源码的最后一行代码;当this.lazy为true的时候不会调用订阅者的get方法;当this.lazy为false时,才会调用订阅者的get方法;
注意点:
initComputed方法中新建计算属性订阅者(Watcher)的lazy属性为true,所以执行不会调用get方法;
当访问计算属性时,通过Watcher的evaluate方法调用Watcher的get方法;
Watcher.prototype.get = function get () {
//把Watcher放入targetStack数组中,并作为当前的Dep.target值;
pushTarget(this);
var value;
var vm = this.vm;
try {
//调用wacher定义时,传入的第二个参数里面的函数
//传入两个vm,第一个是this的替换,第二个是参数
value = this.getter.call(vm, vm);
} catch (e) {
if (this.user) {
handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\""));
} else {
throw e
}
} finally {
// "touch" every property so they are all tracked as
// dependencies for deep watching
if (this.deep) {
traverse(value);
}
popTarget();
this.cleanupDeps();
}
return value
};
1 计算属性的订阅者(Watcher)的getter方法为计算属性的定义函数;
在实例学习Vue源码第一篇-实例实例中,getter方法内容如下:
reversedChildMessage: function () {
// `this` 指向 vm 实例
return this.childMessage.split('').reverse().join('');
}
2 数据属性的订阅者(Watcher)的getter方法为渲染的Watcher,gtter方法的内容如下:
updateComponent = function () {
vm._update(vm._render(), hydrating);
};
3 当Watcher和发布者的依赖关系建立完毕后,就会把当前Watcher出栈;
popTarget();
/**
* Clean up for dependency collection.
*/
Watcher.prototype.cleanupDeps = function cleanupDeps () {
var i = this.deps.length;
while (i--) {
var dep = this.deps[i];
if (!this.newDepIds.has(dep.id)) {
dep.removeSub(this);
}
}
var tmp = this.depIds;
this.depIds = this.newDepIds;
this.newDepIds = tmp;
this.newDepIds.clear();
tmp = this.deps;
this.deps = this.newDeps;
this.newDeps = tmp;
this.newDeps.length = 0;
};
1 保存newDepIds到depIds后,清掉或重置newDepIds ;
2 保存newDeps到deps 后,清掉或重置newDeps ;
/**
* Subscriber interface.
* Will be called when a dependency changes.
*/
Watcher.prototype.update = function update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true;
} else if (this.sync) {
this.run();
} else {
queueWatcher(this);
}
};
1 当给数据属性重新设新值时,会属性的发布者(Dep)会notify订阅者(Watcher)调用update函数;
2 计算属性的订阅者(W atcher)的lazy值为true,因为不会调用调用queueWatcher方法;
3 数据属性会调用queueWatcher方法,把更新操作放入微观任务队列中,等待主线程空闲时,通过回调函数执行订阅者(Watcher)的run方法;
4 如果this.sync为true,就直接调用run方法,一般不会采用同步调用;
/**
* Push a watcher into the watcher queue.
* Jobs with duplicate IDs will be skipped unless it's
* pushed when the queue is being flushed.
*/
function queueWatcher (watcher) {
var id = watcher.id;
if (has[id] == null) {
has[id] = true;
if (!flushing) {
queue.push(watcher);
} else {
// if already flushing, splice the watcher based on its id
// if already past its id, it will be run next immediately.
var i = queue.length - 1;
while (i > index && queue[i].id > watcher.id) {
i--;
}
queue.splice(i + 1, 0, watcher);
}
// queue the flush
if (!waiting) {
waiting = true;
if (!config.async) {
flushSchedulerQueue();
return
}
nextTick(flushSchedulerQueue);
}
}
}
把订阅者(Watcher)放入任务队列(queue)中;
has[id] == null的判断,用来对同一个时间片内的重复的设置操作进行去重,只执行一次;
function nextTick (cb, ctx) {
var _resolve;
callbacks.push(function () {
if (cb) {
try {
cb.call(ctx);
} catch (e) {
handleError(e, ctx, 'nextTick');
}
} else if (_resolve) {
_resolve(ctx);
}
});
if (!pending) {
pending = true;
timerFunc();
}
// $flow-disable-line
if (!cb && typeof Promise !== 'undefined') {
return new Promise(function (resolve) {
_resolve = resolve;
})
}
}
1 nextTick函数会把flushSchedulerQueue函数放入callbacks队列中;
2 nextTick函数调用timerFunc产生一个微观任务,微观任务的回调函数:
function flushCallbacks () {
pending = false;
var copies = callbacks.slice(0);
callbacks.length = 0;
for (var i = 0; i < copies.length; i++) {
copies[i]();
}
}
循环遍历callbacks队列,执行队列中保存的所有回调函数(flushSchedulerQueue)
function flushSchedulerQueue () {
currentFlushTimestamp = getNow();
flushing = true;
var watcher, id;
queue.sort(function (a, b) {
return a.id - b.id; });
// do not cache length because more watchers might be pushed
// as we run existing watchers
for (index = 0; index < queue.length; index++) {
watcher = queue[index];
if (watcher.before) {
watcher.before();
}
id = watcher.id;
has[id] = null;
watcher.run();
...
}
当主线程空闲时间,从队列中取出订阅者,调用订run方法
/**
* Scheduler job interface.
* Will be called by the scheduler.
*/
Watcher.prototype.run = function run () {
if (this.active) {
var value = this.get();
if (
value !== this.value ||
isObject(value) ||
this.deep
) {
// set new value
var oldValue = this.value;
this.value = value;
if (this.user) {
try {
this.cb.call(this.vm, value, oldValue);
} catch (e) {
handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\""));
}
} else {
this.cb.call(this.vm, value, oldValue);
}
}
}
};
run函数中会调用订阅者(Watcher)的get函数,重新更新视图
updateComponent = function () {
vm._update(vm._render(), hydrating);
};