vue3源码-三、ref和toRefs的实现

发布时间 2023-05-06 22:16:31作者: 楸枰~

实现Ref

ref的本质就是通过类属性访问器来实现,可以将一个普通值类型进行包装

import { hasChanged, isObject } from "@vue/shared";
import { track, trigger } from "./effect";
import { TrackOpTypes, TriggerOpTypes } from "./operations";
import { reactive } from "./reactive";

export function ref(value) { // ref Api
 return createRef(value);
}

export function shallowRef(value) { // shallowRef Api
 return createRef(value, true);
}
function createRef(rawValue, shallow = false) {
 return new RefImpl(rawValue, shallow)
}

const convert = (val) => isObject(val) ? reactive(val) : val; // 递归响应式

// 实现类
class RefImpl {
 private _value; // 保存值
 public readonly __v_isRef = true; // 标识是ref
 constructor(private _rawValue, public readonly _shallow) {
     // 初始化value
     this._value = _shallow ? _rawValue : convert(_rawValue)
 }
 get value() {
     // 依赖收集
     track(this, TrackOpTypes.GET, 'value');
     return this._value;
 }
 set value(newVal) {
     if (hasChanged(newVal, this._rawValue)) {
         this._rawValue = newVal; // 保存值
         this._value = this._shallow ? newVal : convert(newVal);
         // 触发更新
         trigger(this, TriggerOpTypes.SET, 'value', newVal);
     }
 }
}

实现toRefs

使用:将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的ref。每个单独的ref都是使用 toRef()创建的。

const state = reactive({
foo: 1,
bar: 2
})

const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型:{
foo: Ref<number>,
bar: Ref<number>
}
*/

// 这个 ref 和源属性已经“链接上了”
state.foo++
console.log(stateAsRefs.foo.value) // 2

stateAsRefs.foo.value++
console.log(state.foo) // 3

// 解构使用
function useFeatureX() {
const state = reactive({
 foo: 1,
 bar: 2
})

// ...基于状态的操作逻辑

// 在返回时都转为 ref
return toRefs(state)
}

// 可以解构而不会失去响应性
const { foo, bar } = useFeatureX()



class ObjectRefImpl{
 public readonly __v_isRef = true
 constructor(private readonly _object, private readonly _key) {}
 get value(){
     return this._object[this._key]
 }
 set value(newVal){
     this._object[this._key] = newVal
 }
}
export function toRef(object,key){
 return new ObjectRefImpl(object,key);
}
export function toRefs(object) {
 const ret = isArray(object) ? new Array(object.length) : {};
 for (const key in object) {
     ret[key] = toRef(object, key)
 }
 return ret;
}

这样就可以将对象的属性转化为ref属性。