Vue3響應式原理-Ref
Ref的概念
proxy代理的目标必須是非原始值,是以reactive不支援原始值類型。是以我們需要将原始值類型進行包裝。
const flag = ref(false)
effect(()=>{
document.body.innerHTML = flag.value ? 30:'姜文'
});
setTimeout(()=>{
flag.value = true
},1000);
Ref & ShallowRef
function createRef(rawValue, shallow) {
return new RefImpl(rawValue,shallow); // 将值進行裝包
}
// 将原始類型包裝成對象, 同時也可以包裝對象 進行深層代理
export function ref(value) {
return createRef(value, false);
}
// 建立淺ref 不會進行深層代理
export function shallowRef(value) {
return createRef(value, true);
}
function toReactive(value) { // 将對象轉化為響應式的
return isObject(value) ? reactive(value) : value
}
class RefImpl {
public _value;
public dep;
public __v_isRef = true;
constructor(public rawValue, public _shallow) {
this._value = _shallow ? rawValue : toReactive(rawValue); // 淺ref不需要再次代理
}
get value(){
if(activeEffect){
trackEffects(this.dep || (this.dep = new Set)); // 收集依賴
}
return this._value;
}
set value(newVal){
if(newVal !== this.rawValue){
this.rawValue = newVal;
this._value = this._shallow ? newVal : toReactive(newVal);
triggerEffects(this.dep); // 觸發更新
}
}
}
toRef & toRefs
響應式丢失問題
const state = reactive({name: 'jw', age: 30 })
let person = {...state}
effect(()=>{
document.body.innerHTML = person.name +'今年' + person.age +'歲了'
})
setTimeout(()=>{
person.age = 31;
},1000)
如果将響應式對象展開則會丢失響應式的特性
class ObjectRefImpl {
public __v_isRef = true
constructor(public _object, public _key) { }
get value() {
return this._object[this._key];
}
set value(newVal) {
this._object[this._key] = newVal;
}
}
export function toRef(object, key) { // 将響應式對象中的某個屬性轉化成ref
return new ObjectRefImpl(object, key);
}
export function toRefs(object) { // 将所有的屬性轉換成ref
const ret = Array.isArray(object) ? new Array(object.length) : {};
for (const key in object) {
ret[key] = toRef(object, key);
}
return ret;
}
let person = {...toRefs(state)}; // 解構的時候将所有的屬性都轉換成ref即可
effect(()=>{
document.body.innerHTML = person.name.value +'今年' + person.age.value +'歲了'
})
setTimeout(()=>{
person.age.value = 31;
},1000)
自動脫ref
let person = proxyRefs({...toRefs(state)})
effect(()=>{
document.body.innerHTML = person.name +'今年' + person.age +'歲了'
})
setTimeout(()=>{
person.age = 31;
},1000)
export function proxyRefs(objectWithRefs){ // 代理的思想,如果是ref 則取ref.value
return new Proxy(objectWithRefs,{
get(target,key,receiver){
let v = Reflect.get(target,key,receiver);
return v.__v_isRef? v.value:v;
},
set(target,key,value,receiver){ // 設定的時候如果是ref,則給ref.value指派
const oldValue = target[key];
if(oldValue.__v_isRef){
oldValue.value = value;
return true
}else{
return Reflect.set(target,key,value,receiver)
}
}
})
}