vue2.x和vue3.x响应式对比

343 阅读1分钟

vue2.x中的响应式

实现原理:

对象类型:使用 Object.defineProperty() 对对象的属性读取,修改进行拦截(数据劫持);

数组类型:通过重新数组一系列方法来实现拦截(对数组变更的方法进行了包裹);

存在问题:

由于 Object.defineProperty() 对对象的属性只是进行读取和修改,所以当新增属性或者删除属性页面不会更新。同样直接通过下标更改数组页面不会更新;

解决方案:

vue 提供了 $set$delete 两个实例方法,让开发者通过这两个实例方法对已有响应式对象添加或删除属性;

案例展示:

<template>
  <div class="box">
    <h3 class="box-title">实现响应式原理:</h3>
    <div>
      <span>姓名:{{ person.name }}</span>
      <span>年龄:{{ person.age }}</span>
      <span>性别:{{ person.sex }}</span>
      <span>爱好:{{ hoppy }}</span>
    </div>
    <div class="box-button">
      <button @click="deleteAge">删除年龄</button>
      <button @click="addSex">添加性别</button>
      <button @click="changeHoppy">更改爱好</button>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      person: {
        name: '小明',
        age: 18
      },
      hoppy: ['打球', '睡觉']
    }
  },
  methods: {
    // 点击删除年龄
    deleteAge () {
      // console.log(this.person.age) // 18
      // delete this.person.age   //数据变化,视图没有更新
      // console.log(this.person.age) // undefined

      // 解决办法
      this.$delete(this.person, 'age')
    },
    // 点击添加年龄
    addSex () {
      // console.log(this.person.sex) // undefined
      // this.person.sex = '女'  //数据变化,视图没有更新
      // console.log(this.person.sex) // 女

      // 解决办法
      this.$set(this.person, 'sex', '女')
    },
    // 修改爱好
    changeHoppy () {
      // this.hoppy[0] = '学习' // 视图没有更新
      // 由于vue对splice方法进行了包裹,所以使用数组的方法可以实现视图的更新
      // this.hoppy.splice(0, 1, '学习')

      // 也可以使用this.$set()
      this.$set(this.hoppy, 0, '学习')
    }
  }
}
</script>

vue3.x中的响应式

实现原理:

通过 Proxy 代理拦截属性的变化,包括新增,删除,修改,查看。

通过 Reflect 反射对拦截的属性进行操作。

案例展示


 let person = { name: "小明", age: 12 };
    let p = new Proxy(person, {
      //get方法用于拦截对象属性的读取,参数target是源对象,propKey是读取的属性,
      get(target, propKey) {
        //拦截读取属性
        return Reflect.get(target, propKey);
      },
      set(target, propKey, value) {
        //拦截对象属性或者添加新属性
        Reflect.set(target, propKey, value);
      },
      deleteProperty(target, propKey) {
        //拦截删除属性
        return Reflect.deleteProperty(target, propKey);
      },
    });