手撸mini-vue之isRef&unRef&proxyRef

307 阅读1分钟

isRef

判断是不是 ref

单元测试
// ref.spec.ts

describe("ref", () => {
  it("isRef", () => {
    const a = ref(1);
    const b = reactive({ foo: 1 });
    expect(isRef(a)).toBe(true);
    expect(isRef(1)).toBe(false);
    expect(isRef(b)).toBe(false);
  });
})
代码实现
// ref.ts

class RefImpl {
  public __v_isRef = tue;
  ...
}

export function isRef(ref) {
  return !!ref.__v_isRef;
}

这里返回值加上两个感叹号是为了如果参数不是一个 ref,那么 ref.__v_isRef 是 undefined。所以要变成boolean 值

unRef

如果是 ref,返回 .value,否则返回本身

单元测试
// ref.spec.ts

describe("ref", () => {
  it("unRef", () => {
    const a = ref(1);
    expect(unRef(a)).toBe(1);
    expect(unRef(1)).toBe(1);
  });
})
代码实现
// ref.ts

export function unRef(ref) {
  return isRef(ref) ? ref.value : ref;
}

proxyRef

在 vue3 的 template 模板中使用 ref,不需要调用 .vue,就是用到了 proxyRef

// ref.spec.ts

describe("ref", () => {
  it("proxyRefs", () => {
    const user = {
      age: ref(10),
      name: "5c24",
    };

    const proxyUser = proxyRefs(user);
    expect(user.age.value).toBe(10);
    expect(proxyUser.age).toBe(10);
    expect(proxyUser.name).toBe("5c24");

    proxyUser.age = 20;
    expect(proxyUser.age).toBe(20);
    expect(user.age.value).toBe(20);

    proxyUser.age = ref(10);
    expect(proxyUser.age).toBe(10);
    expect(user.age.value).toBe(10);
  });
})

从单元测试可以知道当用 proxyRefs 包裹的对象 proxyUser 可以直接 .age 获得值,而 user 获取 age 值需要 user.age.value
当 proxyUser 的 age 值赋了一个不管是不是 ref 的值, user.age 的值都会跟着变

代码实现
// ref.ts

export function proxyRefs(objectWithRef) {
  return new Proxy(objectWithRef, {
    get(target, key) {
      // 如果访问的是 ref 类型 返回 .value,如果不是 ref ,返回本身的值
      return unRef(Reflect.get(target, key));
    },
    
    set(target, key, value) {
      if(isRef(target[key]) && !isRef(value)) {
        // 如果新给值不是一个 ref 类型,把当前对象需要赋值的属性为 ref 的 .value 改掉
        return (target[key].value = value);
      } else {
        // 如果新值是一个 ref,直接替换
        return Reflect.set(target, key, value);
      }
    },
  })
}

源码地址戳这里