• Vue响应式系统的基本原理


    响应式系统

    Vue.js 是一款 MVVM 框架,数据模型仅仅是普通的 JavaScript 对象,但是对这些对象进行操作时,却能影响对应视图,它的核心实现就是「响应式系统」。

    Object.defineProperty

    首先我们来介绍一下 Object.defineProperty,Vue.js就是基于它实现「响应式系统」的。

    首先是使用方法:

    /*
        obj: 目标对象
        prop: 需要操作的目标对象的属性名
        descriptor: 描述符
        
        return value 传入对象
    */
    Object.defineProperty(obj, prop, descriptor)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    descriptor的一些属性,简单介绍几个属性,具体可以参考 MDN 文档。

    enumerable,属性是否可枚举,默认 false。
    configurable,属性是否可以被修改或者删除,默认 false。
    get,获取属性的方法。
    set,设置属性的方法。
    实现 observer(可观察的)
    知道了 Object.defineProperty 以后,我们来用它使对象变成可观察的。

    在 init 的阶段会进行初始化,对数据进行「响应式化」。

    为了便于理解,我们不考虑数组等复杂的情况,只对对象进行处理。

    首先我们定义一个 cb 函数,这个函数用来模拟视图更新,调用它即代表更新视图,内部可以是一些更新视图的方法。

    function cb (val) {
        /* 渲染视图 */
        console.log("视图更新啦~");
    }
    
    • 1
    • 2
    • 3
    • 4

    然后我们定义一个 defineReactive ,这个方法通过 Object.defineProperty 来实现对对象的「响应式」化,入参是一个 obj(需要绑定的对象)、key(obj的某一个属性),val(具体的值)。经过 defineReactive 处理以后,我们的 objkey 属性在「读」的时候会触发 reactiveGetter 方法,而在该属性被「写」的时候则会触发 reactiveSetter 方法。

    function defineReactive (obj, key, val) {
      // 递归
      observe(val)
      // 对传入对象进行拦截
      Object.defineProperty(obj, key, {
        enumerable: true,       /* 属性可枚举 */
        configurable: true,     /* 属性可被修改或删除 */
        get: function reactiveGetter () {
          return val	/* 实际上还会依赖收集 */
        },
        set: function reactiveSetter (newVal) {
          if (newVal !== val) {
            cb(newVal)
            observe(newVal)		/* 递归, 进行响应化处理 */
            val = newVal
          }
        }
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    当然这是不够的,我们需要在上面再封装一层 observer 。这个函数传入一个 value(需要「响应式」化的对象),通过遍历所有属性的方式对该对象的每一个属性都通过 defineReactive 处理。

    function observe (obj) {
      if (typeof obj !== 'object' || obj === null) {
        return
      }
    
      Object.keys(obj).forEach(key => {
          defineReactive(obj, key, obj[key])
      })
     }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    最后,让我们用 observer 来封装一个 Vue 吧!

    在 Vue 的构造函数中,对 options 的 data 进行处理,这里的 data 想必大家很熟悉,就是平时我们在写 Vue 项目时组件中的 data 属性(实际上是一个函数,这里当作一个对象来简单处理)。

    class Vue {
      constructor (options) {
        // 保存选项
        this.$options = options
        this.$data = options.data
    
        // 响应化处理
        observe(this.$data)
    
        // 代理 $data,代理后可以直接访问 Mvue 中的数据。
        proxy(this, '$data')
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    为了能类似 Vue 直接访问到 data 中的数据,例如 this.text = 'test',我们需要对 Vue 构造函数中的 data 做一层代理(这里看不明白的可以多看几遍)。

    function proxy (vm, $data) {	/* vm:代理到的对象上,这里是 Vue实例本身;$data 为代理的对象 */
      Object.keys(vm[$data]).forEach(key => {
        // 给vm设置代理属性,将$data中的key代理到vm的属性中
        Object.defineProperty(vm, key, {
          get () {
            return vm[$data][key]
          },
          set (newVal) {
            vm[$data][key] = newVal
          }
        })
      })
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这样我们只要 new 一个 Vue 对象,就会将 data 中的数据进行「响应式」化。如果我们对 data 的属性进行下面的操作,就会触发 cb 方法更新视图。

    let a = new MVue({
      data: {
        test: "I am test"
      }
    })
    
    a.test = "hello, world"		/* 视图更新啦~ */
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    至此,响应式原理已经介绍完了。

    注:本文所有代码参考 Vue响应式系统基本原理

    声明

    • 本文属于读书笔记一类,是作者在 《剖析 Vue.js 内部运行机制》 阅读学习途中,以文中内容为蓝本,辅以个人微末的道行“填写”完成,推荐购买原著阅读,定有收获。
    • 欢迎大佬斧正。
  • 相关阅读:
    centos安装python3/pip3项目所需的第三方模块(在线安装&&离线安装)
    从 0-1 聊聊网络的演进
    数据库查询优化器,RBO优化规则介绍及示例
    【C语言】职工管理系统详解(文件操作)
    川贝母修饰卵清蛋白(Fritillaria thunbergii-OVA/Ovalbumin )
    FST工控软件平台解读
    VScode 右键没有转到定义等的菜单
    2.MySQL 安装
    HbuilderX表格加粗没有效果怎么回事
    深度学习-虚拟机当服务器的安装环境
  • 原文地址:https://blog.csdn.net/qq_45934504/article/details/127667469