vue设计与实现 第6章 ref 响应原理 笔记
ref 函数实现代码
const a = ref(1); function ref(value){ const wrapper = {value} Object.defineProperty(wrapper,'__v_isRef',{ value: true }) return reactive(wrapper) }
把新建一个对象,value 属性是原始值,再把对象交给 reactive 进行响应式处理。所以要获取 ref 的值要拿取其 value 属性。 这里还添加了 不可遍历、配置、改写的属性 __v_isRef。标识 ref 类型。
ref 还能够用于响应式丢失问题
解构和展开运算符 会让响应式数据失去响应 下面是例子
const obj = reactive({ foo: 1, bar: 2 });
let { foo, bar } = obj;
const target = {
...obj,
};
foo = 123;
target.foo = 2;
console.log(obj);
const a = ref(1);
let { value } = a;
value = 456;
console.log(a);

打印结果可以看出,解构和展开的属性已经没了和原来的响应式数据的联系,如何继续保持这种联系呢? 可用 toRef 和 toRefs API
toRef 代码实现
function toRef(target,key){ const wrapper = { get value(){ return Reflect.get(target,key) }, set value(newvalue){ return Reflect.set(target,key,newvalue) } } Object.defineProperty(wrapper,'__v_isRef',{ value: true }) return wrapper }
toRef 就是把对象的某个属性转为 ref 。实现和 ref 函数差不多,只不过 ref 是把新建的对象 交给 reactive 。
而 toRef 是改写新建的对象的 value 属性的 get 方法和 set 方法,关联上目标对象属性。
toRefs 代码实现
function toRefs(target){ const wrapper = {} for (let key in target) { wrapper[key] = toRef(target,key); }return wrapper }
toRefs 只是循环目标对象属性,调用 toRef
现在又有了新的问题,虽然把响应式丢失解决了,但是必须通过 value属性 才能访问,例子
const obj = reactive({ foo: 1, bar: 2 });
const target = {...toRefs(obj)};
target.foo.value
解决方案,创建一个代理对象,每次取值时是 ref 类型,返回 value 属性
function proxyRefs(target){ return new Proxy(target,{ get (target,key,receiver) { const res = Reflect.get(target,key,receiver); return res.__v_isRef ? res.value:res }, set (target,key,newVal,receiver){ if(target[key].__v_isRef) { Reflect.set(target[key],value,newVal,receiver) return true } return Reflect.set(target,key,newVal,receiver) } }) }

在vue 组件中,模板能不用 value 取到值,那是因为 setup 函数返回的数据会传递给 proxyRefs 函数进行处理。
reactive 也有自动调用 proxyRefs ,例子
const count = ref(1); const obj = reactive(count); obj.count // 1

浙公网安备 33010602011771号