10_实现reactive和readonly的嵌套对象转换功能

107 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情

10_实现reactive和readonly的嵌套对象转换功能

🙋 Hello,I'm IamZJT!
✍️ 一名菜鸟前端开发工程师!

📦 Github项目地址:zjt-mini-vue3
🖐️ 欢迎 点赞 star,期盼与您并肩前行...

一、单元测试

  1. reactive

    // src/reactivity/tests/reactive.spec.ts
    
    it('nested reactive', () => {
      const original = {
        nested: { foo: 1 },
        array: [{ bar: 2 }]
      };
    
      const observed = reactive(original);
    
      expect(isReactive(observed.nested)).toBe(true);
      expect(isReactive(observed.array)).toBe(true);
      expect(isReactive(observed.array[0])).toBe(true);
    });
    
  2. readonly

    it('nested readonly', () => {
      const original = { foo: 1, bar: { baz: 2 } };
      const wrapped = readonly(original);
    
      expect(isReadonly(wrapped)).toBe(true);
      expect(isReadonly(wrapped.bar)).toBe(true);
    });
    

二、代码实现

为什么嵌套的深层对象没有转换成reactivereadonly呢?

因为Proxy劫持的是对象本身,并不能劫持子对象的变化。

其实通过单测可以看出,我们只需要将reactive嵌套的里层对象也转换成reactive,将readonly嵌套的里层对象也转换成readonly

那只需要在get中,返回res的时候,将res转换成相应的代理就可以了。

function createGetter(isReadonly = false) {
  return function get(target, key) {
    const res = Reflect.get(target, key);

    if (key === ReactiveFlags.IS_REACTIVE) {
      return !isReadonly;
    } else if (key === ReactiveFlags.IS_READONLY) {
      return isReadonly;
    }

    if (isObject(res)) {
      return isReadonly ? readonly(res) : reactive(res);
    }

    if (!isReadonly) {
      track(target, key);
    }

    return res;
  };
}

跑一下完整的单测结果看下:

10_reactive和readonly嵌套对象转换完整单测结果

ps: 其实这里需要注意的一点就是,在get中处理嵌套转换,我们只有在用到这个子对象的时候,才会将这个子对象 转换成响应时代理,避免了不必要的性能浪费。对比vue2的递归遍历defineProperty来说,也是一个优化的地方。

ps

🎯 如果您看到这里,请不要走开。
🎉 这是一个早起俱乐部:三更灯火五更鸡

⭐️ 寻找 志同道合 的小伙伴,我们一起 早起