你知道为什么template中不用加.value吗?
Vue3 中定义的ref
类型的变量,在setup
中使用这些变量是需要带上.value
才可以访问,但是在template
中却可以直接使用。
询其原因,可能会说 Vue 自动进行ref
解包了,那具体如何实现的呢?
proxyRefs
Vue3 中有有个方法proxyRefs
,这属于底层 API 方法,在官方文档中并没有阐述,但是 Vue 里是可以导出这个方法。
例如:
<script setup>
import { onMounted, proxyRefs, ref } from "vue";
const user = {
name: "wendZzoo",
age: ref(18),
};
const _user = proxyRefs(user);
onMounted(() => {
console.log(_user.name);
console.log(_user.age);
console.log(user.age);
});
</script>
上面代码定义了一个普通对象user
,其中age
属性的值是ref
类型。当访问age
值的时候,需要通过user.age.value
,而使用了proxyRefs
,可以直接通过user.age
来访问。
这也就是为何template
中不用加.value
的原因,Vue3 源码中使用proxyRefs
方法将setup
返回的对象进行处理。
实现proxyRefs
单测
it("proxyRefs", () => {
const user = {
name: "jack",
age: ref(10),
};
const proxyUser = proxyRefs(user);
expect(user.age.value).toBe(10);
expect(proxyUser.age).toBe(10);
proxyUser.age = 20;
expect(proxyUser.age).toBe(20);
expect(user.age.value).toBe(20);
proxyUser.age = ref(30);
expect(proxyUser.age).toBe(30);
expect(user.age.value).toBe(30);
});
定义一个age
属性值为ref
类型的普通对象user
。proxyRefs
方法需要满足:
proxyUser
直接访问age
是可以直接获取到 10 。- 当修改
proxyUser
的age
值切这个值不是ref
类型时,proxyUser
和原数据user
都会被修改。 age
值被修改为ref
类型时,proxyUser
和user
也会都更新。
实现
既然是访问和修改对象内部的属性值,就可以使用Proxy
来处理get
和set
。先来实现get
export function proxyRefs(objectWithRefs) {
return new Proxy(objectWithRefs, {
get(target, key) {}
});
}
需要实现的是proxyUser.age
能直接获取到数据,那原数据target[key]
是ref
类型,只需要将ref.value
转成value
。
使用unref
即可实现,unref
的实现参见本专栏上篇文章,文章地址:mp.weixin.qq.com/s/lLkjpK9TG…
get(target, key) {
return unref(Reflect.get(target, key));
}
实现set
export function proxyRefs(objectWithRefs) {
return new Proxy(objectWithRefs, {
get(target, key) {
return unref(Reflect.get(target, key));
},
set(target, key, value) {},
});
}
从单侧中可以看出,我们是测试了两种情况,一种是修改proxyUser
的age
为ref
类型, 一种是修改成不是ref
类型的,但是结果都是同步更新proxyUser
和user
。那实现上也需要考虑这两种情况,需要判断原数据值是不是ref
类型,新赋的值是不是ref
类型。
使用isRef
可以判断是否为ref
类型,isRef
的实现参见本专栏上篇文章,文章地址:mp.weixin.qq.com/s/lLkjpK9TG…
set(target, key, value) {
if (isRef(target[key]) && !isRef(value)) {
return (target[key].value = value);
} else {
return Reflect.set(target, key, value);
}
}
当原数据值是ref
类型且新赋的值不是ref
类型,也就是单测中第 1 个情况赋值为 10,将ref
类型的原值赋值为value
,ref
类型值需要.value
访问;否则,也就是单测中第 2 个情况,赋值为ref(30)
,就不需要额外处理,直接赋值即可。
验证
执行单测yarn test ref
来源:juejin.cn/post/7303435124527333416