欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

vue3.0响应式数据是如何实现的?相比vue2.0中Object.defineProperty()有什么优势?

程序员文章站 2022-07-12 22:32:22
...

Vue3为什么选择Proxy做双向绑定?

数据劫持

什么是数据劫持

数据劫持指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果。

常见的数据劫持的方法

Object.defineProperty(obj,'要监听的属性名',{ set(newVal){ }, get(){ }})

var obj  = {};
Object.defineProperty(obj, 'name', {
       get: function() {
           console.log('我被获取了')
           return val;
       },
       set: function (newVal) {
           console.log('我被设置了')
       }
})
obj.name = 'fei';//在给obj设置name属性的时候,触发了set这个方法
var val = obj.name;//在得到obj的name属性,会触发get方法

Object.defineProperty()的缺点

  • 不能监听数组的变化
let arr = [1,2,3]
let obj = {}
Object.defineProperty(obj, 'arr', {
  get () {
    console.log('get arr')
    return arr
  },
  set (newVal) {
    console.log('set', newVal)
    arr = newVal
  }
})
obj.arr.push(4) // 只会打印 get arr, 不会打印 set
obj.arr = [1,2,3,4] // 这个能正常 set

数组的以下几个方法不会触发 set:
push
pop
shift
unshift
splice
sort
reverse
Vue 把这些方法定义为变异方法 (mutation method),指的是会修改原来数组的方法。与之对应则是非变异方法 (non-mutating method),例如 filter, concat, slice 等,它们都不会修改原始数组,而会返回一个新的数组。Vue 官网有相关文档讲述这个问题

  • 必须遍历对象的每个属性
    使用 Object.defineProperty() 多数要配合 Object.keys() 和遍历,于是多了一层嵌套
    Object.keys(obj).forEach(key => {
      Object.defineProperty(obj, key, {
        // ...
      })
    })
    
  • 必须遍历深层次的对象
    所谓的嵌套对象,是指类似
    let obj = {
      info: {
        name: 'eason'
      }
    }
    

如果是这一类嵌套对象,那就必须逐层遍历,直到把每个对象的每个属性都调用 Object.defineProperty() 为止。 Vue 的源码中就能找到这样的逻辑 (叫做 walk 方法)

为什么vue3.0会使用proxy代理对象实现数据的绑定?

在数据劫持这个问题上,Proxy 可以被认为是 Object.defineProperty() 的升级版。外界对某个对象的访问,都必须经过这层拦截。因此它是针对 整个对象,而不是 对象的某个属性,所以也就不需要对 keys 进行遍历。这解决了上述 Object.defineProperty() 的第二个问题

之所以 Vue3.0 要使用 Proxy 替换原本的 API 原因在于 Proxy 无需一层层递归为每个属性添加代理,一次即可完成以上操作,性能上更好,并且原本的实现有一些数据更新不能监听到,但是 Proxy 可以完美监听到任何方式的数据改变

通过 ES6中的 proxy对象: let proxy = new Proxy(obj, handler)

let obj = {
  name: 'Eason',
  age: 30
}
let handler = {
  get (target, key, receiver) {
    console.log('get', key)
    return Reflect.get(target, key, receiver)
  },
  set (target, key, value, receiver) {
    console.log('set', key, value)
    return Reflect.set(target, key, value, receiver)
  }
}
let proxy = new Proxy(obj, handler)
proxy.name = 'Zoe' // set name Zoe
proxy.age = 18 // set age 18
相关标签: vue 面试