转载至:彻底理解Vue中的Watcher、Observer、Dep
在数据响应化时,在getter方法中做依赖收集,在setter方法中做派发更新。dep用于存储依赖和派发更新。
思考以下代码
new Vue({
el: '#example',
data(){
return{
obj:{
a:1
}
}
},
})
当我们写下这行代码时,vue将我们在data内定义的obj对象进行依赖追踪.
具体做法为执行new Observer(obj)
//经过上面的代码,我们的obj对象会变为以下的样子
{
obj:{
a:1,
__ob__:{ //Observer 实例
dep:{Dep 实例
subs:[ //存放 Watcher 实例
new Watcher(),
new Watcher(),
new Watcher(),
new Watcher(),
]
}
}
}
}
我们来一步步实现看下。
在obj
对象上新增__ob__
属性,值为Observe
类的实例,我们编写一个 def
函数,用来增加属性
function def(obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
});
}
增加啥属性呢?之前提到了,我们需要增加一个 Observer 实例,实现如下
Observe 实现
const Dep = require('./Dep')
class Observer {
constructor(targetObject) {
def(targetObject, '__ob__', this);//在 targetObject 上 添加 Observer 实例, setter时 通知该实例
this.walk(targetObject)
this.dep = new Dep()
}
walk(obj) {
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key])
});
}
}
function observe(data) {
if (Object.prototype.toString.call(data) !== '[object Object]') {
return
}
new Observer(data)
}
function defineReactive(obj, key, val) {
observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
console.log('get');
const ob = this.__ob__
ob.dep.depend();
return val
},
set: function reactiveSetter(newVal) {
console.log('set');
if (newVal === val) return
val = newVal
observe(newVal)
const ob = this.__ob__
ob.dep.notify();
},
})
}
function def(obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
});
}
module.exports = Observer
这里面牵扯到了 Dep,我们也把Dep实现下
Dep
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
depend() {
this.subs.push(Dep.target);
}
notify() {
for (let i = 0; i < this.subs.length; i++) {
this.subs[i].fn();
}
}
}
Dep.target = null;
module.exports=Dep
Observer
类 主要做了以下事情
- 遍历 data 下的每一个属性,若是对象,则 执行
new Observer()
,在对象上新增__ob__
属性,该属性的值为Observer
的实例 - 劫持对象属性的变化,在
getter
的时候,拿到Observer
实例的dep
实例,执行dep.depend()
,代码如下
const ob = this.__ob__
ob.dep.depend();
看下 dep.depend()做了些啥
this.subs.push(Dep.target)
将Dep.target
添加到 订阅数组内(this.subs)
也就是说,只要我们 Dep.target 赋值了,再执行
dep.depend()
,那么该值就会被添加到 dep 的 subs 数组内,比如
Dep.target =function test(){}
dep.depend()
// test 函数就算 Dep 的订阅者了
实现自动添加依赖
这个时候该 Watcher出场了
Watcher 实现
const Dep = require("./Dep");
class Watcher {
constructor(vm, exp, fn) {
this.vm = vm;
this.exp = exp;
this.fn = fn;
Dep.target = this; //将自己挂载到 Dep.target,调用 Dep.depend时会读取该变量
this.vm[exp];
}
update() {
//加入队列
}
}
module.exports = Watcher;
根据一个小例子来理解 Watcher
const obj = {
a: 1,
b: {
c: 2
}
}
new Observer(obj)
new Watcher(obj, 'a', () => {
console.log('Watcher 回调执行')
})
obj.a='222'
流程如下:
- 先观测 obj 对象(
new Observer(obj)
) - 实例化
Watcher
时,会执行Dep.target = this
,然后执行this.vm[exp]
,也就是取一次值,那么会触发getter,
将自身(Watcher实例)添加到dep的订阅者数组内
最后,改变数据时候,触发setter
set: function reactiveSetter(newVal) {
if (newVal === val) return
val = newVal
observe(newVal)
const ob = this.__ob__
ob.dep.notify();
},
执行ob.dep.notify()
notify() {
for (let i = 0; i < this.subs.length; i++) {
this.subs[i].fn()
}
遍历 订阅者(subs)执行回调函数,整个流程结束