1.ref
功能
ref
与reactive
功能类似,都是将数据变为响应式,ref
通常用来定义基本类型数据,如字符串、数字、布尔值等。而reactive
用来定义对象(或数组)类型数据。虽然ref
也可以用来定义对象或数组类型的数据,但内部会通过reactive
转为代理对象。ref
在script
标签中必须.value
才能使用 在template
标签中则不用
使用
let flag = ref(false);
effect(() => {
app.innerHTML = flag.value ? "111" : "222";
});
setTimeout(() => {
flag.value = true;
}, 1000);
实现
使用类实现,在get
方法中收集effect
,在set
时触发收集到的effect
import { activeEffect, trackEffect, triggerEffects } from "./effect";
import { toReactive } from "./reactive";
import { createDep } from "./reactiveEffect";
export function ref(value) {
return createRef(value);
}
function createRef(value) {
return new RefImpl(value);
}
class RefImpl {
public __v_isRef = true; //ref 标识
public _value; // 用来保存ref的值
public dep = undefined; // 用于收集对应的effect
constructor(public rawValue) {
this._value = toReactive(rawValue);
}
get value() {
trackRefValue(this);
return this._value;
}
set value(newValue) {
if (newValue != this.rawValue) {
this.rawValue = newValue;
this._value = newValue;
triggrtRefValue(this);
}
}
}
function trackRefValue(ref) {
if (activeEffect) {
trackEffect(
activeEffect,
(ref.dep = createDep(() => (ref.dep = undefined), "undefined"))
);
}
}
function triggrtRefValue(ref) {
let dep = ref.dep;
if (dep) {
triggerEffects(dep);
}
}
2.toRef
功能
toRef
函数可以将一个响应式对象的属性转换为一个独立的ref
对象。- 返回的是一个指向源对象属性的
ref
引用,任何对该引用的修改都会同步到源对象属性上。 - 使用
toRef
时需要传入源对象和属性名作为参数。
首先定义一个reactive
对象,将对象进行结构赋值重组后,得到的对应不再具有响应式特性
let state = reactive({ name: "cwj", age: 18 });
console.log(state, {...state});
使用toRef
转换对象中的某个属性,返回一个ObjectRefImpl
let name = toRef(state, "name"); // 基于proxy取值
let age = toRef(state, "age");
console.log(name,name.value);
console.log(age.value);
实现
class ObjectRefImpl {
public __v_isRef = true;
constructor(public _object, public _key) {}
get value() {
return this._object[this._key];
}
set value(newValue) {
this._object[this._key] = newValue;
}
}
export function toRef(object, key) {
return new ObjectRefImpl(object, key);
}
toRefs
功能
toRefs
函数可以将一个响应式对象转换为一个普通的对象,该对象的每个属性都是独立的ref
对象。- 返回的对象可以进行解构,每个属性都可以像普通的
ref
对象一样访问和修改,而且会保持响应式的关联。 toRefs
的使用场景主要是在将响应式对象作为属性传递给子组件时,确保子组件可以正确地访问和更新这些属性。
使用
let state = reactive({ name: "cwj", age: 18 });
let { name, age } = toRefs(state);
console.log(name, age);
实现
export function toRefs(object) {
const res = {};
for (let key in object) {
res[key] = toRef(object, key);
}
return res;
}
proxyRefs
功能
前面我们提到ref
数据在template
中使用时不需要像在script
标签中.value
才能使用
使用:主要用于template中,此处为验证功能
let state = reactive({ name: "cwj", age: 18 });
let proxy = proxyRefs({ ...toRefs(state),a:20 });
proxy.age = 100
proxy.a = 11
effect(() => {
console.log(proxy.name, proxy.age, proxy.a);
});
实现
export function proxyRefs(objectWithRef) {
return new Proxy(objectWithRef, {
get(target, key, receiver) {
let r = Reflect.get(target, key, receiver);
return r.__v_isRef ? r.value : r;
},
set(target, key, value, receiver) {
const oldValue = target[key];
if (oldValue.__v_isRef) { // 如果老值还是一个ref
oldValue.value = value;
return true
} else {
return Reflect.set(target, key, value, receiver);
}
},
});
}