[Vue] Object.defineProperty 什么情况监听不到?和 Proxy 响应式原理又何区别?

前言

Vue 2.x 采用的是 Object.defineProperty 来实现响应式系统,它只能监听已经存在的属性,无法监听对象属性的新增或删除。

Vue 3 使用 Proxy 拦截对对象和数组的访问和修改,实现了响应式系统。它通过拦截这些操作,追踪哪些数据被访问、修改,从而在数据变化时通知相关的依赖。

Object.defineProperty

<template>
  <div class="hello">
    {{ user }}
    <button @click="changeVal">Change name</button>
    <button @click="addVal">Add new Val</button>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  data() {
    return {
      user: {
        name: "Alice"
      }
    };
  },
  methods: {
    changeVal() {
      this.user.name = "Bob";
    },
    addVal() {
      this.user.age = 25; // 视图不会更新
    }
  }
};
</script>

<style scoped></style>

通过 this.$set 新增属性:

this.$set(this.user, "age", 25);

Object.defineProperty 对数组的监听能力有限,push、pop、shift、unshift、splice、sort 和 reverse 数组操作 Vue 可以监听到,而其他的则不行,比如直接修改数组的某个索引值:

this.arrNums[1] = 10;  // 不能监听,视图不会更新

Proxy

reactive 是通过 Proxy 实现的。通过 Proxy 来拦截对象的访问和修改操作,实现对数据的追踪和更新。

const handler = {
  // 拦截属性的读取操作
  get(target, key) {
    console.log(`Getting property ${key}: ${target[key]}`);
    return target[key];
  },

  // 拦截属性的设置操作(包括新增属性)
  set(target, key, value) {
    if (!target.hasOwnProperty(key)) {
      console.log(`Adding new property ${key} with value ${value}`);
    } else {
      console.log(`Updating property ${key} to ${value}`);
    }
    target[key] = value;
    return true;  // 必须返回 true,表示成功
  },

  // 拦截属性的删除操作
  deleteProperty(target, key) {
    console.log(`Deleting property ${key}`);
    delete target[key];
    return true;  // 必须返回 true,表示成功
  }
};

// 创建一个代理对象
const person = new Proxy({}, handler);

// 新增属性
person.name = 'Alice';  // 控制台输出: Adding new property name with value Alice
person.age = 25;        // 控制台输出: Adding new property age with value 25

// 修改现有属性
person.name = 'Bob';    // 控制台输出: Updating property name to Bob

// 读取属性
console.log(person.name);  // 控制台输出: Getting property name: Bob
                           // 输出: Bob

// 删除属性
delete person.age;         // 控制台输出: Deleting property age

当属性在对象中不存在时,通过 set 方法新增该属性。

当属性已经存在,再次通过 set 修改时,拦截器会识别为更新操作。

posted @ 2024-09-13 16:38  Himmelbleu  阅读(11)  评论(0编辑  收藏  举报