Vue 3 中该选 ref() 还是 reactive()?

ref()与reactive()有什么区别?

原文地址:Ref() vs Reactive() in Vue 3 — what’s the right choice?,2022.01.29,by Bartosz Salwiczek

Vue Composition API 提供了两种创建响应式状态的方式:ref()reactive()。你可能会好奇,在开发中到底该选哪一个?甚至是两者都用?我们需要更深入一些,理解它们的区别、适用场景,并做出合理选择。

以下是对你提供的文章内容的全面润色和改写,在不改变原意的前提下,使语言更流畅、更具可读性,适合用于博客或技术文档中发布:


快速了解:ref 与 reactive 的本质

无论是 ref() 还是 reactive(),它们的核心作用是让 Vue 能够追踪状态的变化,一旦变量更新,Vue 会自动重新渲染使用该变量的部分。

看下面这个例子,点击按钮后 personRefpersonReactivename 都会变成 “Amy”,而普通对象 person 则不会触发视图更新。

<template>
  {{ person.name }} <!-- 不会变 -->
  {{ personRef.name }} <!-- 会变为 Amy -->
  {{ personReactive.name }} <!-- 会变为 Amy -->
  <button @click="changeName('Amy')">Change Name</button>
</template>

<script setup lang="ts">
import { ref, reactive } from 'vue'

const person = { name: 'John' }
const personRef = ref({ name: 'John' })
const personReactive = reactive({ name: 'John' })

const changeName = (name: string) => {
  person.name = name
  personRef.value.name = name
  personReactive.name = name
}
</script>

简而言之:ref()reactive() 都是为了让变量具备响应性,以响应视图的变化。


核心区别

1. 支持的数据类型不同

  • ref():支持任意类型,包括基本类型(如字符串、数字、布尔值)和对象。
  • reactive()只能接受对象
// 无效:reactive 不支持原始类型
const x = reactive(true)

// 有效
const y = ref(true)

两者都可以包装对象:

const obj1 = ref({ name: 'John' })     
const obj2 = reactive({ name: 'John' }) 

2. ref 有 .value 属性,reactive 没有

使用 ref() 时,需要通过 .value 获取或修改数据;而 reactive() 则可以直接访问属性。

// reactive 示例
const person = reactive({ name: 'John' })
person.name = 'Amy'

// ref 示例
const personRef = ref({ name: 'John' })
personRef.value.name = 'Amy'

🔎 小提示:在模板中使用 ref 时,不需要写 .value,Vue 会自动解包。


3. ref 支持整个值替换,reactive 不支持

// reactive 无法整体替换对象
let x = reactive({ name: 'John' })
x = reactive({ todo: true }) // Vue 不会追踪新对象的变动

// ref 可以直接替换整个值
const y = ref({ name: 'John' })
y.value = { todo: true } // 正常响应

为何需要同时存在两者?

看上去 reactive() 像是功能更少的 ref(),那为什么还要用它呢?其实,Vue 团队有其考虑。

我们可以看看 Vue 源码中 ref() 的实现:

class RefImpl<T> {
  private _value: T
  private _rawValue: T
  ...
  constructor(value: T, isShallow: boolean) {
    this._rawValue = isShallow ? value : toRaw(value)
    this._value = isShallow ? value : toReactive(value)
  }
}

你没看错,ref() 本质上就是把数据转换成 reactive() 的包装,也就是说:

const myRef = reactive({ value: "I'm ref!" })
myRef.value = 'Changed'

所以可以理解为:ref 是基于 reactive 实现的封装


reactive 的最佳使用场景

优点:避免频繁使用 .value

使用多个 ref 时,需要不断地访问 .value,看起来有些冗余。reactive() 则更接近 Vue 2 中的 data() 结构,更加直观。

例如:

// 使用 reactive 管理组件状态
const state = reactive({
  user: { name: 'John' },
  isLoading: false,
  hasError: false
})

相比之下,如果使用 ref()

const user = ref({ name: 'John' })
const isLoading = ref(false)
const hasError = ref(false)

后者会造成变量和模板中频繁使用 .value,并且不如集中式的管理清晰。


性能方面的区别

使用多个 ref() 变量会导致整个对象在某个属性变动时被整体更新;而 reactive() 能对每个属性进行追踪,只更新有变动的部分,这样更高效。


总结:如何选择?

使用场景 推荐使用
管理多个独立的值 ref()
管理单个状态对象 reactive()
需要响应式的原始类型 ref()
不想频繁写 .value reactive()
需要整体替换状态对象 ref()

个人观点

虽然 ref()reactive() 在功能上有重合,但它们各自适用于不同的开发习惯:

  • 如果你更喜欢统一风格、使用频率更广,推荐优先使用 **ref()**。
  • 如果你希望更接近 Vue 2 的写法、状态集中管理,或者避免 .value 的使用,那么 reactive() 是更好的选择。

我个人倾向默认使用 ref(),甚至很多情况下用了reactive()后一些情况还需要toRef()把对象解构出来,因为 .value 可以作为“响应式标识”,让代码可读性更强。


你也可以理解为:

  • ref():适合单值响应(原始值/复杂对象)
  • reactive():适合状态容器(多个字段统一管理)

欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1701220998@qq.com
导航页 GitHub