isRef
是用于判断一个对象是否被ref实例对象所包裹;
export function isRef(r: any): r is Ref {
return !!(r && r.__v_isRef === true)
}
unRef
省略ref调用值时的.value操作,直接进行数据的操作获取;
当使用.value太频繁的时候,不知道后面的值到底有没有.value,此时就可以用该API进行包裹访问
export function unref<T>(ref: MaybeRef<T> | ComputedRef<T> | ShallowRef<T>): T {
return isRef(ref) ? ref.value : ref
}
proxyRefs
当访问的是 ref 类型的值时 返回.value;
当访问的不是 ref 类型的值时 直接返回value;
帮助我们脱离ref的value限制,让我们可以直接访问响应式数据,而不需要通过value间接访问
在Vue3中的setup中,不管在模板中有没有使用.value,都可以进行.value的省略,因为setup内部返回的结果就调用了这个API
-
proxyRefs需求分析
- proxyRefs可以对数据进行get和set,并且还有对应的处理
- 在get的时候,会自动把.value省略掉
- 在set的时候,需要区分是普通值还是ref;proxyUser.age = ref(10)
功能实现
-
由于需要劫持对象,因此需要使用Proxy来进行代理劫持
-
在进行get的时候,判断获取的结果是ref还是普通的,如果是ref的话,默认调用.value
-
对于set的话,同样需要判断set的内容是不是ref,且需要判断set之前的值是什么类型的
- set的是内容是普通值,且原来的值是ref,需要调用.value来赋值
- 否则,直接替换即可
export function proxyRefs<T extends object>(
objectWithRefs: T,
): ShallowUnwrapRef<T> {
return isReactive(objectWithRefs)
? objectWithRefs
: new Proxy(objectWithRefs, shallowUnwrapHandlers)
}
const shallowUnwrapHandlers: ProxyHandler<any> = {
get: (target, key, receiver) => unref(Reflect.get(target, key, receiver)),
set: (target, key, value, receiver) => {
const oldValue = target[key]
// set需要判断新内容和set之前的内容是否为ref
if (isRef(oldValue) && !isRef(value)) {
// 新值是普通值,原来的值是ref类型 - 调用.value来替换
oldValue.value = value
return true
} else {
// 直接替换
return Reflect.set(target, key, value, receiver)
}
},
}
shallowRef
只处理基本数据类型的响应式,不进行对象的响应式处理
- 有一个对象数据,后续功能不会修改该对象中的属性,而是生成新的对象来替换时使用shallowRef
- 当使用该API代理对象的时候,对象默认是不带有响应式的,需要进行强制更新页面DOM的操作 - triggerRef
const state = shallowRef({ count: 1 })
// 不会触发更改
state.value.count = 2
// 会触发更改
state.value = { count: 2 }
export function shallowRef<T>(
value: T,
): Ref extends T
? T extends Ref
? IfAny<T, ShallowRef<T>, T>
: ShallowRef<T>
: ShallowRef<T>
export function shallowRef<T = any>(): ShallowRef<T | undefined>
export function shallowRef(value?: unknown) {
return createRef(value, true)
}
function createRef(rawValue: unknown, shallow: boolean) {
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shallow)
}
class RefImpl<T> {
private _value: T
private _rawValue: T
public dep?: Dep = undefined
public readonly __v_isRef = true
constructor(
value: T,
public readonly __v_isShallow: boolean,
) {
this._rawValue = __v_isShallow ? value : toRaw(value)
this._value = __v_isShallow ? value : toReactive(value)
}
get value() {
trackRefValue(this)
return this._value
}
set value(newVal) {
const useDirectValue =
this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
newVal = useDirectValue ? newVal : toRaw(newVal)
if (hasChanged(newVal, this._rawValue)) {
const oldVal = this._rawValue
this._rawValue = newVal
this._value = useDirectValue ? newVal : toReactive(newVal)
triggerRefValue(this, DirtyLevels.Dirty, newVal, oldVal)
}
}
}
toRef
访问代理:解决响应式数据丢失的问题
可以为源响应式对象上的某个属性新创建一个ref,且ref可以被传递,会保持对源属性的响应式连接
import { ref, toRef } from "vue";
// 使用 toRef 后 两个变量数据 会产生链式关系 互相响应 一个数据发送改变 另一个也会跟随 改变
const user = ref({
name: "Lbxin",
age: 22,
});
const newAge = toRef(user.value, "age");
newAge.value = 20;
console.log(user.value.age); // 20
user.value.age = 18;
console.log(newAge.value); // 18
实现思路
接收两个参数,第一个是响应式数据,第二个是响应式数据的一个键,通过返回类似Ref结构的对象进行响应式获取
// 访问子属性.value相当于访问父对象.子属性 底层还是proxy
export function toRef(
source: Record<string, any> | MaybeRef,
key?: string,
defaultValue?: unknown,
): Ref {
if (isRef(source)) {
return source
} else if (isFunction(source)) {
return new GetterRefImpl(source) as any
} else if (isObject(source) && arguments.length > 1) {
return propertyToRef(source, key!, defaultValue)
} else {
return ref(source)
}
}
function propertyToRef(
source: Record<string, any>,
key: string,
defaultValue?: unknown,
) {
const val = source[key]
return isRef(val)
? val
: (new ObjectRefImpl(source, key, defaultValue) as any)
}
class GetterRefImpl<T> {
public readonly __v_isRef = true
public readonly __v_isReadonly = true
constructor(private readonly _getter: () => T) {}
get value() {
return this._getter()
}
}
toRefs
访问代理:解决响应式数据丢失的问题
将一个响应式对象转换为普通对象,但是内部的数据还是响应式的
import { ref, toRefs } from "vue";
// 使用 toRef 后 两个变量数据 会产生链式关系 互相响应 一个数据发送改变 另一个也会跟随 改变
const user = ref({
name: "Lbxin",
age: 22,
});
const newAge = toRefs(user.value);
newAge.age.value = 20;
console.log(user.value.age); // 20
user.value.age = 18;
console.log(newAge.value); // 18
实现思路
- 将响应式数据转换为类似于ref结构的数据
- 在某些方面上来说,toRefs转换后的结果要视为真正的ref数据
export function toRefs(object) {
const res = {};
for (let key in object) {
// 挨个属性调用toRef
res[key] = toRef(object, key);
}
return res;
}