Vue.js 源码分析 数据响应式原理-Observer

201 阅读2分钟

「这是我参与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这个类的核心作用就是对数组/对象做响应式的处理