1.入口Observer函数
- data中的属性会被传入Observer函数
- Observer函数,递归为每一个属性执行defineReactive函数后,每一个节点都会被包装为一个观察者。
var Observer = function Observer (value) {
this.value = value;
this.dep = new Dep();
this.vmCount = 0;
def(value, '__ob__', this);
if (Array.isArray(value)) {
var augment = hasProto
? protoAugment
: copyAugment;
augment(value, arrayMethods, arrayKeys);
this.observeArray(value);
} else {
this.walk(value);
}
};
Observer.prototype.walk = function walk (obj) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i]);
}
};
2.defineReactive实现数据劫持
function defineReactive (
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 ("development" !== 'production' && customSetter) {
customSetter();
}
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
dep.notify();
}
});
}
- defineReactive函数中会创建一个派发器(new Dep)
- data中属性触发defineProperty.get时会调用 Dep.depend()来收集依赖
- data中属性触发defineProperty.set时会调用 Dep.notify()
3. 订阅者watcher
那么Dep.notify()是如何驱动视图变化的呢?
我从官网找了一张图很清晰的描述了这一过程
Watcher负责做的事情就是订阅Dep当Dep发出消息传递(notify)时,订阅者Dep的Watchers会进行update操作。
export default class Watcher {
vm: Component;
expression: string;
cb: Function;
constructor (
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: Object
) {
this.vm = vm
vm._watchers.push(this)
this.cb = cb
// parse expression for getter
if (typeof expOrFn === 'function') {
this.getter = expOrFn
} else {
// 解析表达式
this.getter = parsePath(expOrFn)
if (!this.getter) {
this.getter = function () {}
}
}
this.value = this.get()
}
get () {
// 将目标收集到目标栈
pushTarget(this)
const vm = this.vm
let value = this.getter.call(vm, vm)
// 删除目标
popTarget()
return value
}
// 订阅 Dep,同时让 Dep 知道自己订阅着它
addDep (dep: Dep) {
const id = dep.id
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id)
this.newDeps.push(dep)
if (!this.depIds.has(id)) {
// 收集订阅者
dep.addSub(this)
}
}
}
// 订阅者'消费'动作,当接收到变更时则会执行
// 最终让视图更新就是使用的该方法
update () {
this.run()
}
run () {
const value = this.get()
const oldValue = this.value
this.value = value
this.cb.call(this.vm, value, oldValue)
}
}
看完代码得知watcher在监听到dep传递来的消息后调用update()函数去触发了视图的变化
Dep 负责收集所有数据变化,通过notify发布消息。
Watcher负责订阅Dep并在订阅的时候让Dep进行收集,接收到 Dep 发布的消息时,做update操作
两者相互独立又相互依赖配合实现了vue中的数据驱动
文章写得比较简单,只是把整个流程的概念讲了一遍。具体的还是得认真研读源码。
end