实现vue3响应式系统核心-shallowReactive

实现vue3响应式系统核心-shallowReactive_第1张图片

简介

今天来实现一下 shallowReactive 这个 API。

reactive函数是一个深响应,当你取出的值为对象类型,需要再次调用 reactive进行响应式处理。很明显我们目前的代码是一个浅响应,即 只代理了对象的第一层,也就是 shallowReactive

代码地址: https://github.com/SuYxh/share-vue3

代码并没有按照源码的方式去进行组织,目的是学习、实现 vue3 响应式系统的核心,用最少的代码去实现最核心的能力,减少我们的学习负担,并且所有的流程都会有配套的图片,图文 + 代码,让我们学习更加轻松、快乐。

每一个功能都会提交一个 commit ,大家可以切换查看,也顺变练习练习 git 的使用。

单元测试

it('深响应 reactive', () => {
  const mockFn = vi.fn();

  const obj = reactive({ foo: { bar: 1 } })

  effect(function effectFn() {
    console.log(obj.foo.bar);
  })
  expect(mockFn).toHaveBeenCalledTimes(1);


  obj.foo.bar = 2
  expect(mockFn).toHaveBeenCalledTimes(2);
})

it('浅响应 shallowReactive', () => {
  const mockFn = vi.fn();

  const obj = shallowReactive({ foo: { bar: 1 } })

  effect(function effectFn() {
    console.log(obj.foo.bar);
  })
  expect(mockFn).toHaveBeenCalledTimes(1);


  obj.foo.bar = 2
  expect(mockFn).toHaveBeenCalledTimes(1);
})

代码实现

reactive函数的get中,增加如下判断:

if (typeof res === 'object' && res !== null) {
  return reactive(res)
}

新增一个shallowReactive 函数并导出, 和之前的 reactive函数一样。

运行单测

深响应 reactive:

实现vue3响应式系统核心-shallowReactive_第2张图片

浅响应 shallowReactive:

实现vue3响应式系统核心-shallowReactive_第3张图片

都没有问题!

重构

我们看到 shallowReactivereactive有极大的相似,需进行代码抽离:

export function createReactive(target, isShallow = false) {
  return new Proxy(target, {
    // 拦截读取操作
    get(target, key, receiver) {
      // 代理对象可以通过 raw 属性访问原始数据
      if (key === symbolRaw) {
        return target;
      }

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

      //  如果是浅响应,则直接返回原始值
      if (isShallow) {
        return res;
      }

      if (typeof res === "object" && res !== null) {
        return reactive(res);
      }

      // 依赖收集
      track(target, key);
      return res;
    },
    // 拦截设置操作
    set(target, key, newVal, receiver) {
      // 先获取旧值
      const oldVal = target[key];
      // 如果属性不存在,则说明是在添加新属性,否则是设置已有属性
      const type = Object.prototype.hasOwnProperty.call(target, key)
        ? TriggerType.SET
        : TriggerType.ADD;

      // 设置属性值
      const res = Reflect.set(target, key, newVal, receiver);

      // target === receiver.raw 说明 receiver 就是 target 的代理对象
      if (target === receiver[symbolRaw]) {
        // 较新值与旧值,只有当它们不全等,并且不都是 NaN 的时候才触发响应
        if (oldVal !== newVal && (oldVal === oldVal || newVal === newVal)) {
          trigger(target, key, type);
        }
      }

      return res;
    },
    // 拦截 in 操作符
    has(target, key) {
      track(target, key);
      return Reflect.has(target, key);
    },
    // 拦截 for in 循环
    ownKeys(target) {
      track(target, ITERATE_KEY);
      return Reflect.ownKeys(target);
    },
    // 拦截删除
    deleteProperty(target, key) {
      // 检查被操作的属性是否是对象自己的属性
      const hadKey = Object.prototype.hasOwnProperty.call(target, key);
      // 使用 Reflect.deleteProperty 完成属性的删除
      const res = Reflect.deleteProperty(target, key);

      if (res && hadKey) {
        // 只有当被删除的属性是对象自己的属性并且成功删除时,才触发更新
        trigger(target, key, TriggerType.DEL);
      }

      return res;
    },
  });
}

// 对原始数据的代理
export function reactive(target) {
  return createReactive(target);
}

export function shallowReactive(target) {
  return createReactive(target, true);
}

运行测试

pnpm test

实现vue3响应式系统核心-shallowReactive_第4张图片

重构后的代码也没有问题!

实现vue3响应式系统核心-shallowReactive_第5张图片

一个前端小学生的学习之路,如果你喜欢前端,我们可以一起进行学习、交流、共建。可以添加好友,结伴学习,成长的路上不孤单!

你可能感兴趣的:(你还不会手写,vue,吗,vue,前端框架,javascript)