3. 实现Vue3源码 —— ref与toRef

306 阅读2分钟

1. ref 源码实现

由于reactive只能代码对象,不能支持原始类型,所以vue3提供了一个ref方法方便我们对原始值进行处理;

1.1 ref API使用


import { effect, ref } from "@hpstream/reactivity";

const flag = ref(false);
const nums = ref(1);
const str = ref('1');
effect(() => {
  document.body.innerHTML = flag.value ? "30" : "hp";
});
setTimeout(() => {
  flag.value = true;
}, 1000);


1.2 源码实现:

tof是对于reactive的一个包装,通过get value,和set value的调用reactive,进行依赖收集和分发。

function toReactive(value) {
  // 将对象转化为响应式的
  return isObject(value) ? reactive(value) : value;
}
class RefImpl {
  public __v_isRef = true;
  public _value;
  public deps = new Set();
  constructor(public rawValue) {
    this._value = toReactive(rawValue);
  }
  get value() {
    trackEffects(this.deps);
    return this._value;
  }

  set value(newVal) {
    if (this._value != newVal) {
      this._value = toReactive(newVal);
      triggerEffects(this.deps);
    }
  }
}

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

我们如果理解了reactive和vue3依赖收集的实现,剩下的只是在之前的基础上增加了一些好用的api; 所以代码写起来也会比较简单。

2. toRef 与 toRefs源码类型;

将reactive类型,转换成ref的类型

2.1 toRef API 使用

import { effect, toRefs, toRef, reactive } from "@hpstream/reactivity";

const state = reactive({ name: "jw", age: 30 });

let person = toRefs(state); // 解构的时候将所有的属性都转换成ref即可
let name = toRef(state, "name");
effect(() => {
  document.body.innerHTML =
    person.name.value + "今年" + person.age.value + "岁了";
});
setTimeout(() => {
  name.value = 31;
}, 1000);

reactive 转换成toRef,本质上也是对reactive进行了一个包装。

2.2 toRef 源码实现;

class ObjectRefImpl {
  constructor(private source, private key: string) {}
  get value() {
    return this.source[this.key];
  }

  set value(newVal) {
    if (this.source[this.key] !== newVal) {
      this.source[this.key] = toReactive(newVal);
    }
  }
}
export function toRef(source, key: string) {
  return new ObjectRefImpl(source, key);
}

纯粹用set,get做了一层代码,去操作reactive;

2.3 toRefs的实现

循环调用toRefs就可以实现了。

export function toRefs<T>(
  source: T
): T extends any[] ? ObjectRefImpl[] : Record<keyof T, ObjectRefImpl> {
  if (Array.isArray(source)) {
    let result: any = [];
    for (let item of source) {
      result.push(item);
    }
    return result;
  } else {
    let result: any = {};
    for (let key in source) {
      result[key] = toRef(source, key);
    }
    return result;
  }
}

致谢

如果感觉我的文章有用,请关注下我的公众号: 前端小黄。 我将不定期更新我的原创文章。

本文章源码地址:github.com/hpstream/vu…