一、ref
普通值变成响应式 就需要使用 ref
原理:将普通类型转化成一个对象,这个对象中有 value 属性, 指向原来的值。
ref 和 reactive 的区别: reactive 内部采用 proxy,ref 内部使用的是 defineProperty
//将普通类型变成一个对象
export function ref(value?: unknown) {
return createRef(value) // vue 源码基本上都是高阶函数,做了类似柯里化的功能
}
function createRef(rawValue: unknown, shallow = false) {
if (isRef(rawValue)) {
return rawValue
}
// 返回一个类
return new RefImpl(rawValue, shallow)
}
// 如果是对象就调用 reactive 转成响应式的,否则直接返回
const convert = <T extends unknown>(val: T): T =>
isObject(val) ? reactive(val) : val
// 这个类的底层实现就是 defineproperty
class RefImpl<T> {
private _value: T // 声明了才能在下面用,不声明使用会报错 ,声明但没有赋值
public readonly __v_isRef = true
constructor(private _rawValue: T, public readonly _shallow: boolean) { // 参数前面加 private public修饰符 表示此属性放到了实例上
this._value = _shallow ? _rawValue : convert(_rawValue)
}
// 类的属性访问器
get value() {
track(toRaw(this), TrackOpTypes.GET, 'value')
return this._value // 取值取value,会代理到 _value 上
}
set value(newVal) {
// 判断新值和老值是否有变化
if (hasChanged(toRaw(newVal), this._rawValue)) {
this._rawValue = newVal // 新值会作为老值
this._value = this._shallow ? newVal : convert(newVal) // 如果是深度需要把这个对象全部转成响应式的
trigger(toRaw(this), TriggerOpTypes.SET, 'value', newVal)
}
}
}
二、toRef
toRef 将一个对象中的某一个属性转换成 ref
class ObjectRefImpl<T extends object, K extends keyof T> {
public readonly __v_isRef = true
constructor(private readonly _object: T, private readonly _key: K) {}
get value() {
return this._object[this._key]
}
set value(newVal) {
this._object[this._key] = newVal
}
}
// 将一个 key 对应的值转换成 ref
export function toRef<T extends object, K extends keyof T>(
object: T, // 目标对象
key: K // key 属性
): ToRef<T[K]> {
return isRef(object[key])
? object[key]
: (new ObjectRefImpl(object, key) as any)
}
三、toRefs
将一个对象中的多个属性转成 ref
// 循环调用 toRef
export function toRefs<T extends object>(object: T): ToRefs<T> {
if (__DEV__ && !isProxy(object)) {
console.warn(`toRefs() expects a reactive object but received a plain one.`)
}
const ret: any = isArray(object) ? new Array(object.length) : {}
for (const key in object) {
ret[key] = toRef(object, key)
}
return ret
}
使用场景
toRef 和 toRefs 相当于响应式解构 ,解构某一个就使用 toRef ,解构多个使用 toRefs。