原始值响应式方案
在Javascript中原始值是按照值传递的,而非引用传递,这意味着一个函数接收一个原始值作为参数,形参和实参,是两个独立的值。以及proxy只能代理对象无法对原始值进行代理,因此想要对原始值进行代理,变成响应式数据就得给原始值包裹一层对象。就是vue3中实现的ref。
自定义简易ref
const wrapper = {
value: 'vue'
}
const name = reactive(wrapper)
name.vue = 1 // 触发响应式
封装ref
const { reactive, effect } = require("@vue/reactivity")
function ref(value) {
const wrapper = {
value
}
// 标记为ref 不可枚举
Object.defineProperty(wrapper, '__v_isRef', {
value: true
})
return reactive(wrapper)
}
// 实现
const refVal = ref(1)
effect(() => {
// 收集依赖
console.log(refVal.value, 'refVal')
})
// 修改值触发响应式重新执行
refVal.value = 2
响应式丢失
ref除了能解决原始值的响应式方案以外,还能用来解决响应式丢失问题。
const obj = reactive({ a:1, b: 2})
const {a, b} = obj // 对象的解构以及...运算符都使对象失去响应式
const obj1 = {...obj}
// 等价于
const obj1 = {
a: obj.a,
b: obj.b
}
// 可以使用toRef
function toRef(obj, key) {
const wrapper = {
get value() {
return obj[key]
},
set value(val) {
obj[key] = val
}
}
return wrapper
}
function toRefs(obj) {
const ret = {}
for (let key in obj) {
ret[key] = toRef(obj, key)
}
return ret
}
// toRef 使得解构的数据重新与源对象建立联系 从而解决了响应式丢失的问题
自动脱ref
在vue开发项目中,我们在script中使用ref都是有value的,而在模板中使用自动给我们去掉了.value。大致的实现原理就是在元素抛出去的时候,调用一个函数,来解决这个问题。
// 给ref重新包一个代理,如果值是ref 则在读取obj.a的时候 直接返回obj.a.value
// 这样就实现了自动脱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;
},
set(target, key, newVal, receiver) {
const value = target[key]
if (value.__v_isRef) {
// 如果是ref, 在给对象赋值的时候,自动赋值给ref.value
value.value = newVal
return true
}
return Reflect.set(target, key, newVal, receiver)
}
});
}
const name = ref(1)
const name1 = reactive({name}) // 自动脱ref
在vue的使用场景中,ref转到reactiv的时候也会自动脱ref。