「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战」
数据响应式原理-Observer
本章我们要解析 Observer 对象是怎么进行 响应式处理的
Observer 类
Observer 属性
在类的最上层我们事先定义了三个属性,并设置了属性的类型
-
value: any 观测对象
-
dep: Dep 依赖对象
-
vmCount: number 实例计数器
Observer 构造函数
在构造函数中我们对值进行了对应的初始化
this.value = value
this.dep = new Dep()
// 初始化实例的 vmCount 为0
this.vmCount = 0
并且 将实例挂载到观察对象的 ob 属性
def(value, '__ob__', this)
为何要进行这样的挂载?
这样做的好处就是,我们可以对已经做过了响应式处理的数据打上对应的标识,防止有重复缓存的操作
def 方法解析
export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
})
}
def 方法很简单,我们传入对应的(value, '__ob__', this)将对应的值进行响应式转换后,enumerable可枚举属性设置为不可枚举;这样做的目的是什么?
- 为了不设置 get 和 set 属性
- 我们当前设置的
_ob__属性只是为了记录Observer对象的,所以不需要遍历后设置 get、set
构造函数的核心部分
这里我们要判断 value 是否是数组,如果是数组要对数组进行数组的响应式处理,如果非数组则会调用walk方法
// 数组的响应式处理
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods)
} else {
copyAugment(value, arrayMethods, arrayKeys)
}
// 为数组中的每一个对象创建一个 observer 实例
this.observeArray(value)
} else {
// 遍历对象中的每一个属性,转换成 setter/getter
this.walk(value)
}
walk
walk会获取观察对象的每一个属性,并且遍历每一个属性,设置为响应式数据
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
当然转换成响应式数据主要也是调用了 defineReactive,这个方法中使用了Object.defineProperty为对象属性上添加了 get、set,当然 defineReactive在为我们添加了get、set之余,也做了很多额外的处理:
- 收集依赖
- 数据变化时发送通知等
到这 Observer 类我们就查看的差不多了,也非常的直观,Observer这个类的核心作用就是对数组/对象做响应式的处理