0
点赞
收藏
分享

微信扫一扫

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

Xin_So 2022-03-30 阅读 31

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 监测(修改该变量时,视图也会同步发生改变)。

在这里插入图片描述

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 条评论