摘要:源码解读设计模式系列文章将陆陆续续进行更新中 ~
观察者模式
首先话题下来,我们得反问一下自己,什么是观察者模式?
概念
观察者模式(Observer):通常又被称作为发布-订阅者模式。它定义了一种一对多的依赖关系,即当一个对象的状态发生改变的时候,所有依赖于它的对象都会得到通知并自动更新,解决了主体对象与观察者之间功能的耦合。
讲个故事
上面对于观察者模式的概念可能会比较官方化,所以我们讲个故事来理解它。
- A:是共产党派往国民党密探,代号 001(发布者)
B:是共产党的通信人员,负责与 A 进行秘密交接(订阅者)
- A 日常工作就是在明面采集国民党的一些情报
- B 则负责暗中观察着 A
- 一旦 A 传递出一些有关国民党的消息(更多时候需要对消息进行封装传递,后面根据源码具体分析)
- B 会立马订阅到该消息,然后做一些相对应的变更,比如说通知共产党们做一些事情应对国民党的一些动作。
适用性
以下任一场景都可以使用观察者模式
- 当一个抽象模型有两个方面,其中一个方面依赖于另一方面。讲这两者封装在独立的对象中可以让它们可以各自独立的改变和复用
- 当一个对象的改变的时候,需要同时改变其它对象,但是却不知道具体多少对象有待改变
- 当一个对象必须通知其它对象,但是却不知道具体对象到底是谁。换句话说,你不希望这些对象是紧密耦合的。
vue 对于观察者模式的使用
vue
使用到观察者模式的地方有很多,这里我们主要谈谈对于数据初始化这一块的。
var vm = new Vue({
data () {
return {
a: 'hello vue'
}
}
})
1、实现数据劫持
上图我们可以看到,vue
是利用的是 Object.defineProperty()
对数据进行劫持。 并在数据传递变更的时候封装了一层中转站,即我们看到的 Dep
和 Watcher
两个类。
这一小节,我们只看如何通过观察者模式对数据进行劫持。
1.1、递归遍历
我们都知道,vue
对于 data
里面的数据都做了劫持的,那只能对对象进行遍历从而完成每个属性的劫持,源码具体如下
walk (obj: Object) {
const keys = Object.keys(obj)
// 遍历将其变成 vue 的访问器属性
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i], obj[keys[i]])
}
}
1.2、发布/订阅
从上面对象的遍历我们看到了 defineReactive
,那么劫持最关键的点也在于这个函数,该函数里面封装了 getter
和 setter
函数,使用观察者模式,互相监听
// 设置为访问器属性,并在其 getter 和 setter 函数中,使用发布/订阅模式,互相监听。
export function defineReactive (
obj: Object,
key: string,
val: any
) {
// 这里用到了观察者(发布/订阅)模式进行了劫持封装,它定义了一种一对多的关系,让多个观察者监听一个主题对象,这个主题对象的状态发生改变时会通知所有观察者对象,观察者对象就可以更新自己的状态。
// 实例化一个主题对象,对象中有空的观察