浅析从vue源码看观察者模式(2)
1.2、发布/订阅
从上面对象的遍历我们看到了 defineReactive ,那么劫持最关键的点也在于这个函数,该函数里面封装了 getter 和 setter 函数,使用观察者模式,互相监听
// 设置为访问器属性,并在其 getter 和 setter 函数中,使用发布/订阅模式,互相监听。 export function defineReactive ( obj: Object, key: string, val: any ) { // 这里用到了观察者(发布/订阅)模式进行了劫持封装,它定义了一种一对多的关系,让多个观察者监听一个主题对象,这个主题对象的状态发生改变时会通知所有观察者对象,观察者对象就可以更新自己的状态。 // 实例化一个主题对象,对象中有空的观察者列表 const dep = new Dep() // 获取属性描述符对象(更多的为了 computed 里面的自定义 get 和 set 进行的设计) const property = Object.getOwnPropertyDescriptor(obj, key) if (property && property.configurable === false) { return } const getter = property && property.get const setter = property && property.set let childOb = observe(val) Object.defineProperty(obj, key, { enumerable: true, configurable: true, // 收集依赖,建立一对多的的关系,让多个观察者监听当前主题对象 get: function reactiveGetter () { const 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) { const value = getter ? getter.call(obj) : val if (newVal === value || (newVal !== newVal && value !== value)) { return } if (setter) { setter.call(obj, newVal) } else { val = newVal } childOb = observe(newVal) dep.notify() } }) }
1.3、返回 Observer 实例
上面我们看到了observe 函数,核心就是返回一个 Observer 实例
return new Observer(value)
2、消息封装,实现 "中转站"
首先我们要理解,为什么要做一层消息传递的封装?
我们在讲解观察者模式的时候有提到它的 适用性 。这里也同理,我们在劫持到数据变更的时候,并进行数据变更通知的时候,如果不做一个"中转站"的话,我们根本不知道到底谁订阅了消息,具体有多少对象订阅了消息。
这就好比上文中我提到的故事中的密探 A(发布者) 和共产党 B(订阅者)。密探 A 与 共产党 B 进行信息传递,两人都知道对方这么一个人的存在,但密探 A 不知道具体 B 是谁以及到底有多少共产党(订阅者)订阅着自己,可能很多共产党都订阅着密探 A 的信息,so 密探 A(发布者) 需要通过暗号 收集到所有订阅着其消息的共产党们(订阅者),这里对于订阅者的收集其实就是一层封装。然后密探 A 只需将消息发布出去,而订阅者们接受到通知,只管进行自己的 update 操作即可。