• Vue2源码学习笔记 - 11.响应式原理—Observer 类详解


    Vue 的响应式功能模块是一个庞大的模块,它涉及了很多其他模块、函数、算法及设计模式,当然,这句话也适用于描述 Vue 本身。在这其中,有几个关键的类,我们在此做简单的学习了解,后续我们仍会使用和研究它们。

    接下来我们继续学习 Vue 响应式的三个关键类:ObserverDepWatcher。其中按其职责和前后,我们先来研究学习 Observer 类,先上UML图熟悉下

    UML

    Vue Observer 类UML图

    使用场景

    Observer 的实例化只在 observe 函数中进行,函数在文件 /src/core/observer/index.js 中,上一节我们已经研究过,传入参数为一个对象或数组。

    export function observe (value: any, asRootData: ?boolean): Observer | void {
      if (!isObject(value) || value instanceof VNode) {
        return
      }
      let ob: Observer | void
      if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
        ob = value.__ob__
      } else if (
        shouldObserve &&
        !isServerRendering() &&
        (Array.isArray(value) || isPlainObject(value)) &&
        Object.isExtensible(value) &&
        !value._isVue
      ) {
        ob = new Observer(value)
      }
      if (asRootData && ob) {
        ob.vmCount++
      }
      return ob
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    Observer

    先看 Observer 类的源码,它在文件 /src/core/observer/index.js 中定义

    ...
    /**
     * Observer class that is attached to each observed
     * object. Once attached, the observer converts the target
     * object's property keys into getter/setters that
     * collect dependencies and dispatch updates.
     */
    export class Observer {
      // 传入实参数据 value 
      value: any;
      // Dep 对象
      dep: Dep;
      // 把 value 当作根 $data 属性的 vm 实例个数
      vmCount: number;
    
      constructor (value: any) {
        // 初始化赋值
        this.value = value
        this.dep = new Dep()
        this.vmCount = 0
        // 给 vaule 值定义 __ob__ 属性,值为本对象,这个后续有用到
        def(value, '__ob__', this)
        if (Array.isArray(value)) {
          // --------------------------------------------
          if (hasProto) {                              //
            protoAugment(value, arrayMethods)          //
          } else {                                     // 数组响应式改造
            copyAugment(value, arrayMethods, arrayKeys)//
          } // ------------------------------------------
          // 数组则遍历元素逐个调用 observe 响应式化
          this.observeArray(value)
        } else {
          // 单个数据对象调用 walk 成员方法
          this.walk(value)
        }
      }
    
      /**
       * Walk through all properties and convert them into
       * getter/setters. This method should only be called when
       * value type is Object.
       */
      walk (obj: Object) {
        const keys = Object.keys(obj)
        // 遍历数据对象所有键,调用 defineReactive 设置为响应式的属性
        for (let i = 0; i < keys.length; i++) {
          defineReactive(obj, keys[i])
        }
      }
    
      /**
       * Observe a list of Array items.
       */
      observeArray (items: Array<any>) {
        // 遍历数组,每个元素传入 observe 响应式化
        for (let i = 0, l = items.length; i < l; i++) {
          observe(items[i])
        }
      }
    }
    
    // helpers
    
    /**
     * Augment a target Object or Array by intercepting
     * the prototype chain using __proto__
     */
    function protoAugment (target, src: Object) {
      /* eslint-disable no-proto */
      target.__proto__ = src
      /* eslint-enable no-proto */
    }
    
    /**
     * Augment a target Object or Array by defining
     * hidden properties.
     */
    /* istanbul ignore next */
    function copyAugment (target: Object, src: Object, keys: Array<string>) {
      for (let i = 0, l = keys.length; i < l; i++) {
        const key = keys[i]
        def(target, key, src[key])
      }
    }
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    Observer 类比较简单,在 new Observer 实例化对象的时候传入了需要响应式处理的数据对象 value,它需要是对象或者数组。在构造函数中对 value 进行了保存,然后给 value 的 ob 属性赋值为本 Observer 对象,这样就把 value 与 Observer 对象关联起来,这个 ob 在后续其他功能需要用到。

    vue Observer 实例化处理

    然后根据 value 为数组或对象做不同处理,如果是数组则遍历并对每个数组元素调用 observe 响应式处理,到最后其实还是对每个数组元素执行 new Observer;如果是对象,则遍历对象的属性值调用 defineReactive 改造成响应式的属性。observe 和 defineReactive 我们前面都有讲解,那么经过 Observer 类处理后,实际就是改造 value 对象的 setter/getter,使它具备响应式功能。

    vue observe函数处理流程图

    关于数组的响应式改造,是通过改造数组的一些方法,它是在 protoAugment 和 copyAugment 函数中进行的,这个我们后面用一个小章节来专门研究学习。

    总结:

    如图所示,Observer 类比较简单,它对数据对象做简单判断然后调用 defineReactive 把对象属性定义为响应式的属性。它的实例对象是与数据对象关联的,这个关联在后面我们继续会说到。

  • 相关阅读:
    代码模版-实现重置按钮清空表单数据,vue+elementUI
    go语言 | 图解反射(二)
    Vue前端面试题
    Stable Diffusion WebUI挂VPN不能跑图解决办法(Windows)
    基于Springboot的超市管理系统毕业设计-附源码
    Maven引用本地jar包
    Spring MVC 中的国际化和本地化
    F. Selling a Menagerie Codeforces Round 895 (Div. 3)
    深度学习 Pytorch安装和基本操作
    合合TextIn - 大模型加速器
  • 原文地址:https://blog.csdn.net/dclnet/article/details/126009108