【Vue | 补洞 | 21】Vue2 监测数据原理(含简单实现)

  Vue 的核心在于:数据更新会引起视图更新。前提就在于,Vue 需要知道数据何时发生了变化。它是如何监测到的?


1. 原理

  通过 Object.defineProperty 实现对数据变化的监测

  这么说总有些晦涩难懂,来个小故事:

  这天,你在宿舍玩游戏,你喊了一下你舍友,让他要下去吃饭的时候叫上你一起。饭点一到,你的舍友就来叫你,哥们吃饭去!

  上面的故事,你给你的舍友设置了一个"属性",让他吃饭时记得来叫你。这时候你哪怕玩游戏正入迷着,也能监测到舍友要去吃饭的这个信息。

  同理,Object.defineProperty 能添加一个可被监听的属性,一旦这个属性发生变化,Vue 就能知道并且做后续操作。


2. 探索

  首先给出结论:在 Vue 中,能看到分配了 getset 方法的变量,都是可以被 Vue 所监测到变化的变量。带着结论,我们往下探索。

  在任意页面中,声明一个 age 变量,并在 mounted 中输出 this

// xxx.vue
mounted() {
  console.log(this)  
},
data () {
    return {
      age: 18
    }
}

1)this 是什么?

  可以看到,this 输出了一个组件实例对象(也就是你的 xxx.vue 文件)
在这里插入图片描述

2)age 是否有对应的 get 和 set?

  展开来查看组件实例对象中的 _data,可以看到其中包含了 age 变量,及其 getset 方法,这便说明了该变量可被 Vue 监测(修改该变量时,视图也会同步发生改变)。

  在数据代理那篇文章,讲解过 _data 的相关内容,还未读过的小伙伴务必复习一下:【Vue | 补洞 | 09】数据代理

在这里插入图片描述

3)age 后面为什么是 (...)

  我们都知道需要鼠标点击省略号,才能显示出 age 中的内容;点击的这个操作,就相当于读取了 age,触发了 get 方法

在这里插入图片描述

4)什么时候触发 set

  当我们通过如 this.age = xxx 来对 age 赋值时,便会触发 set 方法

5)触发 get 和 set 后,Vue 会做什么?

  • 触发 get 方法:Vue 就可以读取最新的值,并显示在视图上
  • 触发 set 方法:在修改数据后,Vue 就会去重新渲染视图,将最新的数据显示在界面上

6)我只定义了 age,但是看到的 _data 却包含了 get 和 set?

  Vue 在底层将 age 做了转换,将变量转变为 可监测的数据对象 Observer,简单过程理解为:const newAge = new Observer(age)newAge 就能被监测到变化了。


3. 简单实现

  我们来实现一个将 age 转换为 newAge 的函数,实现数据变化监测

1)Object.defineProperty(obj, prop, options) 用法

  • obj:需要被监测变化的 对象
  • prop:需要被监测变化的 属性
  • options:可以配置 get 和 set 方法
let obj = {}

Object.defineProperty(obj, 'name', {
    get() {
        console.log(`触发get方法,读取值为${obj[key]}`)
        return obj[key]
    },
    set(val) {
        console.log(`${key}被改变了`)
        obj[key] = val
    }
})

2)Observer 函数的简单实现

let data = {
    name: '波吉',
    address: '福州',
}

const obs = new Observer(data)

let vm = {}
vm._data = data = obs

// 用一个构造函数将对象转换为可监测的对象
function Observer(obj) {
    let keys = Object.keys(obj)

    keys.forEach((key) => {
        Object.defineProperty(this, key, {
            get() {
                console.log(`触发get方法,读取值为${obj[key]}`)
                return obj[key]
            },
            set(val) {
                console.log(`${key}被改变了`)
                obj[key] = val
            },
        })
    })
}

3)效果

① 初始值为

在这里插入图片描述

② 读取属性时

在这里插入图片描述

③ 修改属性的值时

在这里插入图片描述

4. 备注

  • 在对象中,不管层叠多少层,或者数组中包含的对象属性,只要属性修改都可以被 Vue 监测到
  • 修改了属性后,一旦触发了 set 方法,Vue 会重新解析模板、虚拟DOM对比等一系列操作,再重新渲染到界面上
  • 注意!Object.defineProperty 无法监测到属性的新增删除,示例如下

在这里插入图片描述
在这里插入图片描述

  • 为解决直接通过 data.xxx 无法添加响应式的情况,Vue 提供了一个 API 可以用于给对象新增响应式属性,用法 this.$set(this.data, 'age', 19)


5. 总结

  • Vue2 通过 Object.defineProperty 实现响应式数据监听
  • 响应式数据的共同特点:有 getset 方法
  • Object.defineProperty 的设计缺陷:
    • 无法监测属性的新增和删除
    • 无法监测数组的变化(后续文章会讲解到 如何监测数组变化
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值