一起学Vue3源码,实现最简Vue3【11】 - 实现 ref 功能

实现ref功能

什么是ref?
ref其实和reactive一样,都是创建响应式数据的方法。只不过ref是一个单值,只能用 .value 去获取或者修改。

首先,还是老样子,测试文件
1、第一个测试,是创建一个ref数据,并取出。
2、用effect去监听ref,ref类型修改赋值之前相同的值,还会返回之前没修改时候的值。
3、用ref去创建响应式对象,需要处理哪些边缘case。

ref.spec.ts

import { effect } from "../src/effect";
import { reactive } from "../src/reactive";
import { ref } from "../src/ref";

describe("ref", () => {
  it("happy path", () => {
    const a = ref(1);
    expect(a.value).toBe(1);
  });

  it("should be reactive", () => {
    const a = ref(1);
    let dummy;
    let calls = 0;
    effect(() => {
      calls++;
      dummy = a.value;
    });

    expect(calls).toBe(1);
    expect(dummy).toBe(1);

    a.value++;
    expect(calls).toBe(2);
    expect(dummy).toBe(2);
    // same value should not trigger
    a.value = 2;
    expect(calls).toBe(2);
    expect(dummy).toBe(2);
  });

  it("should make nested properties reactive", () => {
    const a = ref({
      count: 1,
    });
    let dummy;
    effect(() => {
      dummy = a.value.count;
    });
    expect(dummy).toBe(1);

    a.value.count++;
    expect(dummy).toBe(2);
  });
});

接下来,实现ref.ts
因为调用的时候都是用 .value 去获取,所以ref也是一个类的实现

ref.ts

import { hasChange, isObject } from "../../shared";
import { isTracking, trackEffects, triggerEffects } from "./effect";
import { reactive } from "./reactive";

class RefImpl {
  private _value: any;
  public dep: Set<RefImpl>;
  // 缓存最原始的数据,因为可能接收到单一类型或者对象类型
  private _rawValue: any;

  constructor(value) {
    this._rawValue = value;
    // 如果是对象类型,需要封装成 reactive,这里抽离出转化通用方法
    this._value = convert(value);
    // 进行dep 初始化
    this.dep = new Set();
  }

  get value() {
  	// 收集依赖
    trackRefValue(this);
    return this._value;
  }

  set value(newValue) {
    // hasChange 发生改变才会收集依赖并且赋值;具体用 Object.is 取反实现
    // 用新值和之前原始数据比较
    if (hasChange(newValue, this._rawValue)) {
      // 赋值
      this._rawValue = newValue;
      this._value = convert(newValue);
      // 触发依赖
      triggerEffects(this.dep);
    }
  }
}

function trackRefValue(ref) {
  // 和reactive一样,如果没用effect去监听,就不需要依赖收集,否则会找不到当前的effect实例,即 activeEffect 为 undefined
  if (isTracking()) {
    // 收集依赖,因为ref是单一值 .value 所以直接收集dep即可
    trackEffects(ref.dep);
  }
}

function convert(value) {
  return isObject(value) ? reactive(value) : value;
}

export function ref(value) {
  return new RefImpl(value);
}

shared 通用方法

index.ts

...
export const hasChange = (val, newVal) => {
  return !Object.is(val, newVal);
};

以上,执行测试,通过。

最后

附上git代码地址:mini-vue,欢迎大家踊跃探讨,如果可以的话,帮忙点点 git star,万分感谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值