likes
comments
collection
share

你知道为什么template中不用加.value吗?

作者站长头像
站长
· 阅读数 17

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吗?

这也就是为何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类型的普通对象userproxyRefs方法需要满足:

  1. proxyUser直接访问age是可以直接获取到 10 。
  2. 当修改proxyUserage值切这个值不是ref类型时,proxyUser和原数据user都会被修改。
  3. age值被修改为ref类型时,proxyUseruser也会都更新。

实现

既然是访问和修改对象内部的属性值,就可以使用Proxy来处理getset。先来实现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) {},
  });
}

从单侧中可以看出,我们是测试了两种情况,一种是修改proxyUserageref类型, 一种是修改成不是ref类型的,但是结果都是同步更新proxyUseruser。那实现上也需要考虑这两种情况,需要判断原数据值是不是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类型的原值赋值为valueref类型值需要.value访问;否则,也就是单测中第 2 个情况,赋值为ref(30),就不需要额外处理,直接赋值即可。

验证

执行单测yarn test ref

你知道为什么template中不用加.value吗?