Skip to content

1. vue2 的响应式

  • 核心:
    • 对象: 通过 defineProperty 对对象的已有属性值的读取和修改进行劫持(监视/拦截)
    • 数组: 通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持
js
Object.defineProperty(data, "count", {
  get() {},
  set() {},
});
  • 问题
    • 对象直接新添加的属性或删除已有属性, 界面不会自动更新
    • 直接通过下标替换/添加元素或更新 length, 界面不会自动更新

2. Vue3 的响应式

  • 核心:
    • 通过 Proxy(代理): 拦截对 data 任意属性的任意(13 种)操作, 包括属性值的读写, 属性的添加, 属性的删除等...
    • 通过 Reflect(反射): 动态对代理对象的相应属性进行特定的操作
    • 文档:
js
const p = new Proxy(data, {
  // 拦截读取属性值
  get(target, prop) {
    return Reflect.get(target, prop);
  },
  // 拦截设置属性值或添加新属性
  set(target, prop, value) {
    return Reflect.set(target, prop, value);
  },
  // 拦截删除属性
  deleteProperty(target, prop) {
    return Reflect.deleteProperty(target, prop);
  },
});

p.name = "tom";
html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Proxy 与 Reflect</title>
  </head>
  <body>
    <script>
      const user = {
        name: "John",
        age: 12,
      };

      /* 
    proxyUser是代理对象, user是被代理对象
    后面所有的操作都是通过代理对象来操作被代理对象内部属性
    */
      const proxyUser = new Proxy(user, {
        get(target, prop) {
          console.log("劫持get()", prop);
          return Reflect.get(target, prop);
        },

        set(target, prop, val) {
          console.log("劫持set()", prop, val);
          return Reflect.set(target, prop, val); // (2)
        },

        deleteProperty(target, prop) {
          console.log("劫持delete属性", prop);
          return Reflect.deleteProperty(target, prop);
        },
      });
      // 读取属性值
      console.log(proxyUser === user);
      console.log(proxyUser.name, proxyUser.age);
      // 设置属性值
      proxyUser.name = "bob";
      proxyUser.age = 13;
      console.log(user);
      // 添加属性
      proxyUser.sex = "男";
      console.log(user);
      // 删除属性
      delete proxyUser.sex;
      console.log(user);
    </script>
  </body>
</html>