在上篇文章中 我们了解了响应式原理,并在最后实现了reactive。
上文链接Vue3响应式原理实现与track和trigger依赖收集和触发依赖
在我们的日常业务中,我们有可能需要将一个基础数据类型的值也转换成响应式的,而reactive只能代理对象,我们需要对基础数据类型的值也进行读写操作的拦截,但 Proxy 无法实现对基础数据类型值读写操作的拦截。
所以Vue设计了Ref,以及相关api
本篇文章实现下Ref及相关API isRef
,unRef
,proxyRefs
先实现下它.value这个东西,在vue源码中是将ref的值包裹在一个RefImp类中实现的
class RefImp {
private _value: any;
constructor(value) {
// 判断 value 值是不是对象,如果是对象,则用 reactive 包裹
this._value = isObject(value) ? reactive(value) : value;
}
get value() {
// 直接返回 _value
return this._value;
}
set value(value) {
// 设置 _value
this._value = value;
}
}
function ref(value) {
return new RefImp(value);
}
//试验一波
const obj = { a: 1 };
const refObj = ref(obj); console.log(refObj.value); // 输出: { a: 1 }
export class RefImp {
private _value: any;
public dep: any;
constructor(value) {
// / 看看value 是不是一个对象,如果是一个对象的话则转为响应式对象
this._value = isObject(value) ? reactive(value) : value
this.dep = new Set()
}
get value() {
TrackEffects(this.dep)
return this._value
}
set value(newvalue) {
// 检查新值是否发生变化
if (!Object.is(newvalue,_value)) {
this._value = isObject(newValue) ? reactive(newValue) : newValue
TriggerEffect(this.dep)
}
}
}
// `TrackEffects` 函数用于追踪依赖关系,而 `TriggerEffect` 函数用于触发相关效果。
//这是从track和trigger中抽离出来的
function TrackEffects(dep: any) {
if (!dep.has(activeEffect)) {
dep.add(activeEffect)
activeEffect.deps.push(dep)
}
}
export function TriggerEffect(dep) {
for (const effect of dep) {
effect.run()
}
}
新传入的值不可能相等,所以需要有个rawValue存储原来的传入的value与newValue对比
export class RefImp {
...
private rawValue: any;
...
constructor(value) {
this.rawValue = value
...
}
set value(newValue){
if(!Object.is(newValue,this.rawValue)){
this.rawValue = newValue
this._value = isObject(newValue) ? reactive(newValue) : newValue
...
}
}
}
检查某个值是否为 ref。我们可以在refImp类中加入属性来判断
class refImp{
public _v_isRef = true;
...
}
function isRef(value){
return !!value._v_isRef
}
如果参数是 ref对象,则返回内部值,否则返回参数本身。
function(value){
return isRef(value) ? value.value : value
}
前言我们说到有一点 为什么template中不需要.value,
因为Vue使用 proxyRefs
来处理响应式引用的模板解析。proxyRefs
是一个内部函数,用于处理模板中的响应式引用。它会将传入的响应式引用对象转换为代理对象,从而在模板中直接访问引用的属性时能够自动获取其 value
属性的值
接下来实现它
export function proxyRefs(objectWithRefs) {
return new Proxy(objectWithRefs, {
get(target, key) {
return unRef(Reflect.get(target, key))
},
set(target, key, value) {
if (isRef(target[key]) && !isRef(value)) {
return target[key].value = value
} else {
return Reflect.set(target, key, value)
}
}
})
}
文章到这里就结束了,希望对你有所帮助。