聊聊业内争论不休的ref和reactive

引言

refreactive 可以称做是 Vue 响应式 API 中的周瑜和诸葛亮。那么问题来了,你会怎么选?本来将讨论 ref 和 reactive 的区别,并说一下我个人对于这件事的看法。

选谁呢?

一、ref 和 reactive 的基础概念

1. ref 是什么?

ref 是将基本类型数据(如字符串、数字、布尔值等)转换为响应式对象。

  • ref 主要用于创建单一的值,或者是一个不可变的引用。
  • 它返回的是一个包含 value 属性的对象,value 里存放着你传给 ref 的值。
1
2
3
4
5
6
7
8
9
import { ref } from "vue";

const count = ref(0);

console.log(count.value); // 0

// 修改值时直接操作 .value
count.value = 5;
console.log(count.value); // 5

2. reactive 是什么?

reactive 是将对象和数组转为响应式数据。可以直接操作对象或数组,而不需要使用 value,而且会自动追踪这些对象内部的变化。

  • reactive 适用于复杂数据结构,比如对象和数组。
  • 直接对对象的属性进行修改,它会自动变成响应式,视图也会随着变化而更新。
1
2
3
4
5
6
7
8
9
10
11
12
import { reactive } from "vue";

const person = reactive({
name: "Wyx",
age: 27,
});

console.log(person.name); // Wyx

// 修改对象的属性
person.age = 28;
console.log(person.age); // 28

二、ref 和 reactive 的区别

1. 数据类型支持不同

  • ref 一般用于简单数据类型(比如数字、字符串、布尔值),也可以用于复杂数据类型(底层依然是使用reactive去实现)。
  • reactive 仅用于对象、数组这类复杂数据类型。

2. 使用方式不同

  • 使用 ref 时,数据存放在 value 中,所以需要通过 value 来访问或修改值。
  • 使用 reactive 时,数据就是直接访问的,和普通的对象或数组一样,不需要 value。

3. 对象的响应式

  • ref 只能对基本数据类型进行响应式处理,如果你需要对对象进行响应式处理,应该使用 reactive。
  • 如果你把一个对象传给 ref,它会变成一个对象的引用,而不是直接响应式对象。
1
2
3
4
5
6
7
8
const person = ref({
name: "Wyx",
age: 27,
});

console.log(person.value.name); // Wyx
person.value.age = 28;
console.log(person.value.age); // 28

上面这段代码我们依然需要通过 value 来修改对象的属性,这是因为我们用 ref 包装了整个对象。

4. 性能差异

  • reactive 会对对象进行深度代理,这意味着对象的所有嵌套属性都会变成响应式的。所以,如果对象的嵌套层级很深,可能会稍微影响性能。
  • ref 则比较轻量,它只会对单个数据进行响应式处理。

三、什么时候使用 ref,什么时候使用 reactive?

  1. 使用 ref:
    • 当你只有一个简单的原始值(如数字、字符串、布尔值等)时,使用 ref。
    • 比如,计数器、切换状态这样的场景。
1
2
const count = ref(0);
const isActive = ref(true);
  1. 使用 reactive:
    • 当你需要操作对象或者数组时,使用 reactive。
    • 比如,存储表单数据、用户信息等。
1
2
3
4
const user = reactive({
name: "Wyx",
age: 26,
});

四、ref 和 reactive 混用的场景

我们也可以把 ref 和 reactive 结合使用,用于处理复杂数据结构。例如,你有一个对象,它的某个属性是基本数据类型,你可以使用 ref 来包装这个基本数据类型,其他部分让 reactive 去处理。

1
2
3
4
5
6
7
const state = reactive({
count: ref(0),
user: { name: "Wyx", age: 26 },
});

state.count.value = 5;
console.log(state.count.value); // 5

五、产生分歧的原因

每个人有自己的技术偏好,这很正常,不必要非得论个高低。 正如以前讨论 JavaScript 是否需要加分号一样,大家会有不同意见。

呼声较高的认为只用 ref 就可以,反正 Vue3 的相应式系统已经很强大了,搞那么麻烦干啥。反正能用reactive写的,ref也能搞定。
但也有一部分开发者觉得应该分开用,特别是当你需要引用复杂对象时,reactive表现得更好,因为它会把整个对象变成响应式,不用担心某个属性会错过被追踪的情况。而 ref 适合那种简单的、原始的类型,比如count、isActive,不容易搞错。

六、我的看法

产生这种分歧有其历史背景。尤大曾提到,reactive 主要是为了照顾 Vue 2 用户的使用习惯,毕竟在 Vue 2 中,大家习惯性地使用 this.xxx 进行数据访问和操作。而 reactive 的体验会更接近 Vue 2,允许你把各种东西都放进去,这也间接表明了,暴露 reactive 这个 API 其实并非必需的。

个人来说,我更倾向于尤大所说的全程使用 ref。混合使用 reactive 和 ref 确实会出现一些问题,尤其是有些开发者会把所有东西都丢到 reactive 里,久而久之会增加重构的难度。相比之下,ref 的使用则更加灵活,且两个 ref 声明之间没有强绑定的关系,使得重构和维护更加方便。

另外,从团队协作的角度来看,协同性也非常重要。大家如果遵循一套统一的开发规则,也能够大大减少冲突和混乱。