跳到主要内容

认识

2024年03月18日
柏拉文
越努力,越幸运

一、认识


Vue.js 3.0 中, ref 对原始值 BooleanNumberBigIntStringSymbolundefinednull 通过访问器 get 和设置器 set 实现响应式。注意: Proxy 无法代理原始值。监听策略如下:

  1. 通过 get 访问器来收集该值对应副作用函数

  2. 通过 set 设置器来触发该值对应副作用函数

二、类细节


2.1 class

get value

class Test{
get value(){

}
}

const test = new Test();
console.log(test.value);

set value

class Test{
set value(newValue){

}
}

const test = new Test();
test.value = xxx;

2.2 RefImpl

RefImpl 数据结构

RefImpl = {
dep: Set 集合,
__v_isRef: boolean,
__v_isShallow: boolean,
_rawValue: {}, // 原始值
_value: {}
}

三、数据细节

3.1 复杂数据

复杂数据类型的 RefImpl

class RefImpl {
constructor(value) {
this._value = value;
}
get value() {
console.log('访问');
return this._value;
}
set value(newValue) {
console.log('设置');
this._value = newValue;
}
}

const ref1 = new RefImpl({ a: 1 });
console.log(ref1.value.a);
ref1.value.a = 2;
console.log(typeof ref);

RefImpl 传入一个复杂数据类型时, 更改复杂数据类型的属性值, 其实触发的也是 getter 函数, 不会触发 setter 函数。

复杂数据类型的响应性

constructor(value: T, public readonly __v_isShallow: boolean) {
this._value = __v_isShallow ? value : toReactive(value);
}

export const toReactive = <T extends unknown>(value: T): T => {
return isObject(value) ? reactive(value as object) : value;
};

复杂数据类型Ref 内部通过 reactive 实现响应性。this._value = __v_isShallow ? value : toReactive(value); 针对复杂数据类型, toReactive(value) 返回的是 Proxy 代理对象。然后, 复杂类型的 Ref 的响应性逻辑同 Reactive 一致。

3.2 简单数据

简单数据类型的 RefImpl

class RefImpl {
constructor(value) {
this._value = value;
}
get value() {
console.log('访问');
return this._value;
}
set value(newValue) {
console.log('设置');
this._value = newValue;
}
}

const ref = new RefImpl(5);
console.log(ref.value);
ref.value = 4;

RefImpl 传入一个简单数据类型时, 访问简单数据值, 触发 getter 函数, 更改简单数据值, 触发 setter 函数。注意, 这是简单数据类型的 Ref 与 复杂数据类型 Ref 的一个区别。

简单数据类型的响应性

constructor(value: T, public readonly __v_isShallow: boolean) {
this._value = __v_isShallow ? value : toReactive(value);
}

export const toReactive = <T extends unknown>(value: T): T => {
return isObject(value) ? reactive(value as object) : value;
};

复杂数据类型Ref 内部通过 get value(){}set value(){} 函数实现响应性。 this._value = __v_isShallow ? value : toReactive(value); 针对简单数据类型, toReactive(value) 返回的是自身数据。

四、脱 Ref 细节


所谓自动脱 Ref,指的是属性的访问行为, 如果读取的是一个 ref, 则直接将该 ref 对应的 value 属性值返回。要实现这一功能, 我们需要使用 Proxyref 对象做代理。实现如下:

function proxyRefs(target){
return new Proxy(target,{
get(target,key,receiver){
const value = Reflect.get(target,key,receiver);
return value.__v_isRef ? value.value : value;
}
});
}

const ref1 = proxyRefs({ ...ref(4) });

通过 proxyRefref.value 属性的拦截, 我们可以在模版直接访问一个 ref 的值,而无需通过 value 来访问。