认识
一、认识
在 Vue.js 3.0
中, ref
对原始值 Boolean
、Number
、BigInt
、String
、Symbol
、undefined
和 null
通过访问器 get
和设置器 set
实现响应式。注意: Proxy
无法代理原始值。监听策略如下:
-
通过
get
访问器来收集该值对应副作用函数 -
通过
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
属性值返回。要实现这一功能, 我们需要使用 Proxy
为 ref
对象做代理。实现如下:
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) });
通过 proxyRef
对 ref.value
属性的拦截, 我们可以在模版直接访问一个 ref
的值,而无需通过 value
来访问。