Vue-数据监听-对象监听

之前说到了 Object.defineProperty()  用来监听 data 的数据改变,从而实现响应式,但是在 回顾definePropety方法 博客中,出现了一个问题,那就是我在 改变数据,调用 set() 函数的时候,出现了堆栈溢出的问题,这是因为出现了递归的情况。在这里我们就来看看Vue是怎么解决这个问题的。

首先,我们来实现一个简单的数据监听效果,以此来深入理解一下,Vue的数据监听是怎么做到的。

1、新建一个html文件,不需要引入Vue ,然后定义一个对象

<script>
  let data = {
    name: '北京大学',
    address: '北京',
  }
</script>

2、按照之前说过的 Object.defineProperty() 可以用来监听数据来实现一个简易版的

Object.defineProperty(data,'name',{
    get(){},
    set(val){}
  })

乍一看好像是这么回事,但是我data里面不是已经定义过了name属性了么,我再用 Object.defineProperty() 添加一个属性有啥用?当然了,你也可以直接定义一个空对象,例如

<script>
  let data = {}

  Object.defineProperty(data,'name',{
    get(){
      return '北京大学'
    },
    set(val){
      this.name = val
    }
  })
</script>

 那我这么写和之前没啥区别啊,还是堆栈溢出了啊

 所以我们需要换一个思路来解决这个问题,我当时是在外部定义了一个变量,用这个变量来做数据代理,从而避免了堆栈溢出,但事实上,你的 vue 文件中,这么多响应式的数据,肯定不可能像我这样写啊,所以,我们 用了一个新的构造函数来解决这个问题

3、使用  构造函数 Observer 来批量监听数据

function Observer(obj) {
  // 获取对象内部的所有属性键
  const keys = Object.keys(obj)

  // 遍历所有属性
  keys.forEach(key => {

     // 对所有属性进行监听,这里有三个重点,
    // 1、那就是我传给 defineProperty 的第一个参数,并不是之前的data了,而是this对象,这个this代表的就是,构造函数的实例对象 obs 
    // 2、传递给 defineProperty 的第二个参数,也并不需要写死了,而是遍历时,当前的key
    // 3、在 get 函数中,返回的就是与该键对应的对象中的值 data[key]
    // 4、在set函数中,当前的this 也是构造函数的实例对象 obs,但是我们却不需要使用this,而是直接改变传入对象对应的key的值
    Object.defineProperty(this, key, {
      get() {
        return data[key]
      },
      set(val) {
        obj[key] = val
      }
    })
  });
}

4、创建一个监视的对象实例,用于监视data中的属性变化

const obs = new Observer(data)

此时,若是打印 obs 会发现,obs实例对象 内部的所有属性都是变成了 get 和 set 的响应式数据。

 点击属性值查看之后,发现 obs 类中的所有数据都是和data一样,只不过data不是响应式

 那如果我们要像之前说的,需要和Vue中的数据代理类似,那也很简单

// 准备一个vm实例对象
let vm = {}
vm._data = data = obs
console.log(vm, 'vm')

 将辛苦得到的 obs 对象,直接赋值给 原本的data 属性,使得 data 也成为了 响应式的属性。

 之所以是因为我们写的是简化版的数据监听,是因为除了上面的数据代理之外,我们还没有考虑到如果 对象数据内部,还存在对象,那应该怎么办呢?

let data = {
  name: '北京大学',
  adress: '北京',
  a: {
    b: 1
  }
}

还是使用上面的简化版的 Observer() 构造函数,在控制台上可以发现,情况如下

 data 中的最外层属性,都是成为了响应式的,因为都具备了 get 和 set 函数,但是,在查看属性值之后,发现,属性a 的 对象值中的属性并没有成为 响应式的(属性b并没有成为响应式)

这是因为在 我们简写的 Observer() 类中,Object.keys() 只能遍历最外层的属性,对于深层属性无法进行深度遍历,所以深层属性无法 被 Object.defineProperty() 改写。

Vue 则是不同,Vue 内部将 Observer() 类进行完善了,底层采用了递归,对对象内部的所有属性均是进行判断,若是存在 对象属性值,则进行递归,若是基本数据类型,则可以直接加工成为响应式的数据,若是引用数据类型,则还需要其他方法进行加工,这是因为,Vue 将引用数据类型,分为了两种,分别是 对象 和 数组,针对这两种类型各自进行处理。

 至于针对对象和数组的加工方法,我们稍后在详细梳理,因为这涉及到了Vue底层源码问题了。

总结

Vue在监听data内部数据改变时,是通过 遍历data 内部属性,然后根据属性 类型来分别加工,使之成为 响应式数据。

Vue2 版本使用的是 Object.defineProperty() ,以此来将数据转化为响应式。

当数据改变时,触发对应的 set() 方法,然后解析模板,生成新的虚拟DOM,进行新旧虚拟DOM对比,然后渲染新的模板。这就形成了 Vue 的数据影响视图( MVVM )。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值