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

shallowReactive

简介

今天来实现一下 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:

image-20240122004441759

浅响应 shallowReactive:

image-20240122004510851

都没有问题!

重构

我们看到 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

image-20240122004859928

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

引导扫码关注

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

相关推荐

  1. Vue3实现响应编程

    2024-02-01 19:48:01       31 阅读
  2. VUE3-响应

    2024-02-01 19:48:01       37 阅读
  3. Vue3shallowReactive() and shallowReadonly()

    2024-02-01 19:48:01       10 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-01 19:48:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-01 19:48:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-01 19:48:01       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-01 19:48:01       20 阅读

热门阅读

  1. 踩坑日记:opencv多次绘图绘图不全

    2024-02-01 19:48:01       32 阅读
  2. Python中类的成员方法

    2024-02-01 19:48:01       29 阅读
  3. jQuery html的使用

    2024-02-01 19:48:01       26 阅读
  4. R语言入门笔记2.1

    2024-02-01 19:48:01       20 阅读
  5. 24校招,小鹏汽车自动驾驶测试工程师二面

    2024-02-01 19:48:01       23 阅读
  6. MySQL InnoDB是如何实现ACID的?

    2024-02-01 19:48:01       28 阅读
  7. AUTOSAR内存篇 -Flash EEPROM Emulation(FEE)

    2024-02-01 19:48:01       42 阅读
  8. ROS2 学习笔记12:使用 colcon 构建软件包

    2024-02-01 19:48:01       32 阅读
  9. advPython-2

    2024-02-01 19:48:01       29 阅读