亲宝软件园·资讯

展开

详解Vue响应式的部分实现

小婉子啊 人气:0

什么是响应式

简单来说当数据发生变化时,对数据有依赖的代码会重新执行

例如在Vue中,当我们的数据发生改变,界面上对该数据的引用组件会重新渲染

组件data的数据一旦变化,立即出发视图的更新;

computed属性在依赖发生变化时,自动重新计算新值;提供watch监听器,可以监听到数据的变化

Vue2与Vue3响应式之间的区别

使用Object.defineProperty监听对象

该方法允许精确地添加或修改对象的属性,并返回此对象。

Object.defineProperty()方法会在直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象

备注:(应当直接在object构造器对象上调用此方法,而不是在任意一个object类型的实例上调用)

语法:Object.defineProperty(obj, prop, descriptor)

枚举时使用for...inObject.keys方法可以改变这些属性的值,默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改(immutable)的。

对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符

一个描述符只能是这两者其中之一,不能同时是两者

使用Object.defineProperty监听对象

利用 Object.defineProperty 重写 getset,将对象属性的赋值和获取变成函数,我们可以实现一个简单的双向绑定

//实现一个简单的双向绑定
const data = {}
const name = 'xiaowanzi'
Object.defineProperty(data, 'name', {
  get: function () {
    console.log('get')
    return name
  },
  set: function (newVal) {
    console.log('set')
    name = newVal
  }
})
//测试
console.log(data.name)//get xiaowanzi
data.name = 'list'//set

如果我们想让对象的所有属性都具有响应式,就需要对全部属性进行遍历,实现getter和setter:

//实现Vue响应式原理
let obj = {
  name: 'aaa',
  age: 18
}
//获取obj对象的所有key
const keys = Object.keys(obj)//Object.keys()返回一个由一个给定对象的资深可枚举属性组成的数组,数组中的属性名的排列顺序和正常循环遍历该对象时返回的顺序一致
//遍历Key数组,对obj对象的每一个属性进行处理
keys.forEach(key => {
  //使用value变量保存key对应的属性值
  let value = obj[key]
  //使用Object.defineProperty
  Object.defineProperty(obj, key, {
    get() {//当获取属性时,回来到这里
      console.log(`${key}属性被获取`)
      return value
    },
    set(newValue) {//当修改属性时,会来到这里,并且设置的值会传给newValue
      console.log(`${key}属性被修改`)
      //这里不能写成obj[key]=newValue
      //如果这样写相当于又对该属性进行修改值,又会进入set,就死循环了
      value = newValue
    }
  })
})
​
//现在我们已经可以实现监听obj对象的读取与修改了
console.log(obj.name)//在打印'aaa'之前会先打印'name被获取',也就是说监听到属性的获取。
obj.name = 'bbb'//打印name属性被修改,也就是说监听到了属性的改变
​
//实现Vue响应式原理
let obj={
  name:'aaa',
  age:18
}
//获取obj对象的所有key
const keys=object.keys(obj)//Object.keys()返回一个由一个给定对象的资深可枚举属性组成的数组,数组中的属性名的排列顺序和正常循环遍历该对象时返回的顺序一致
//遍历Key数组,对obj对象的每一个属性进行处理
keys.forEach(key=>{
  //使用value变量保存key对应的属性值
  let value = obj[key]
  //使用Object.defineProperty
  Object.defineProperty(obj,key,{
      get(){//当获取属性时,回来到这里
        console.log(`${key}属性被获取`)
        return value
      },
      set(newValue){//当修改属性时,会来到这里,并且设置的值会传给newValue
        console.log(`${key}属性被修改`)
        //这里不能写成obj[key]=newValue
        //如果这样写相当于又对该属性进行修改值,又会进入set,就死循环了
        value=newValue
      }
  })
})
​
//现在我们已经可以实现监听obj对象的读取与修改了
console.log(obj.name)//在打印'aaa'之前会先打印'name被获取',也就是说监听到属性的获取。
obj.name='bbb'//打印name属性被修改,也就是说监听到了属性的改变

缺点

可以实现监听对象的属性,但是它没有办法做到对对象新增的属性进行监听,同时也没有办法做到对数据进行监听

使用ES6的Proxy实现监听对象

该API就是用来实现监听对象的,而且该API对数组同样也是有效果的,在使用Proxy时,通常会搭配Reflect一起使用 Proxy

用于创建代理对象,从而实现基本操作的拦截和自定义(如属性的查找,赋值,枚举,函数调用等)

术语:

语法:

const p = new Proxy(target, handler)

1.第一个参数target:要包装的目标对象

2.第二个参数handle:接收一个对象,内部定义了操作目标对象时的方法;

参数:

方法

Reflect

是一个内置的对象,它提供拦截 JavaScript 操作的方法。Reflect不是一个函数对象,因此它是不可构造的。

通过给对象设置代理,我们可以拦截对象属性的取值/赋值操作。

举个例子:

const student = {
  age: 23
}
const handler = {
  get(target, prop) {
    console.log("读值:", key, value);
    target[key] = value;
    return target[prop]
  },
  set(target, key, value) {
    console.log("设置值", key, value);
    target[key] = value;
    return true
  }
}
const proxy = new Proxy(studengt, handler)
console.log(proxy.age)//23
//proxy.age=32   //32

实现代码

//Proxy+Reflect
let obj = {
  name: 'aaa',
  age: 18
}
//第一个参数为要代理的对象,第二个参数位hander
const proxy = new Proxy(obj, {
  //当访问第一个属性的时候会得到getter
  //同时会传递三个参数
  //target要进行代理对象,这里就是obj
  //key被访问的属性
  //receiver用来绑定this
  get(target, key, receiver) {
    console.log(`${key}属性被访问`)
    return Reflect.get(target, key, receiver)
  },
  //当某一属性修改的时,回来到Setter
  // 同时会传递四个参数
  // target要进行代理的对象,这里就是obj
  // 可以被访问的属性
  // newValue新修改的值
  // receiver用来绑定this
  set(target, key, newValue, receiver) {
    console.log(`${key}属性修改`)
    return Reflect.set(target, key, newValue, receiver)
  }
})
// 以上代码执行完,得到的就是proxy对象就是obj对象的代理
// 我们只需要修改代理对象的就可以做到修改原型对象的效果
// 而且我们对代理对象的修改使我们能够监听到的
console.log(proxy.name)
proxy.name = 'bbb'

加载全部内容

相关教程
猜你喜欢
用户评论