Vue3 用proxy实现响应式

96 阅读1分钟

上一篇文章,我们说了 Vue3 用 Proxy 实现响应式的好处,那么面试的时候可能会进一步深挖,会让候选人手写一个响应式。

手写响应式之前,我们现需要先了解一下 Reflect 的作用,这里总结为 3 点

  • 1.和 Proxy 一样,也是一个内置对象,方法与 proxy handler 的方法相同
  • 2.会替换掉 Object 上的工具函数

示例

const duck = {
  name: 'Maurice',
  color: 'white',
  greeting: function () {
    console.log(`Quaaaack! My name is ${this.name}`);
  },
};
// 检测一个对象是否存在特定属性
Reflect.has(duck, 'color');
// true
Reflect.has(duck, 'haircut');
// false

// 返回这个对象自身的属性
Reflect.ownKeys(duck);
// [ "name", "color", "greeting" ]

// 为这个对象添加一个新的属性;
Reflect.set(duck, 'eyes', 'black');
// returns "true" if successful

// 删除属性
var obj = { x: 1, y: 2 };
Reflect.deleteProperty(obj, 'x'); // true
obj; // { y: 2 }

var arr = [1, 2, 3, 4, 5];
Reflect.deleteProperty(arr, '3'); // true
arr; // [1, 2, 3, , 5]

// 如果属性不存在,返回 true
Reflect.deleteProperty({}, 'foo'); // true

// 如果属性不可配置,返回 false
Reflect.deleteProperty(Object.freeze({ foo: 1 }), 'foo'); // false

了解完之后,那么下直接贴一下代码

// 创建响应式
function reactive(target = {}) {
  // 不是对象或数组,则返回
  if (typeof target !== 'object' || target == null) {
    return target;
  }

  // 代理配置
  const proxyConf = {
    get(target, key, receiver) {
      // 只处理本身(非原型的)属性
      const ownKeys = Reflect.ownKeys(target);

      const result = Reflect.get(target, key, receiver);

      // 深度监听
      return reactive(result);
    },
    set(target, key, val, receiver) {
      // 重复的数据,不做处理
      if (val === target[key]) return true;

      const ownKeys = Reflect.ownKeys(target);

      const result = Reflect.set(target, key, val, receiver);
      return result;
    },
    deleteProperty(target, key) {
      const result = Reflect.deleteProperty(target, key);
      console.log('delete property', key);
      return result; // 是否删除成功
    },
  };

  // 生成代理对象
  const observed = new Proxy(target, proxyConf);
  return observed;
}

// 测试数据
const data = {
  name: 'tailiang',
  age: 20,
  info: {
    province: 'beijing',
    city: 'haidian',
    a: {
      b: {
        c: {
          d: {
            e: 370,
          },
        },
      },
    },
  },
};

const proxyData = reactive(data);