Vue3 中ref和reactive的核心区别是什么?

Vue3 中refreactive的核心区别,这是掌握组合式 API 的关键知识点!我会从「本质差异→使用场景→代码示例→选择原则」四个维度,用通俗的语言帮你彻底分清两者,还会解决你可能遇到的 "解构丢失响应式" 等实际问题。

一、核心区别总览(先记结论)

refreactive都是 Vue3 创建响应式数据的 API,但核心定位不同:

  • ref :主打「基本类型」(String/Number/Boolean),也支持复杂类型(对象 / 数组),通过 .value 访问 / 修改;
  • reactive :主打「复杂类型」(对象 / 数组),无需 .value,直接操作属性,但不支持基本类型。

下面用表格直观对比所有差异点:

对比维度 ref reactive
适用数据类型 基本类型(优先)、复杂类型(兼容) 仅复杂类型(对象 / 数组),不支持基本类型
访问 / 修改方式 逻辑中:变量.value;模板中:直接用 逻辑 + 模板中:直接操作属性(无需.value)
解构特性 解构后仍保持响应式(需注意赋值) 直接解构会丢失响应式(需配合toRefs
新增属性支持 支持(复杂类型时,内部自动用 reactive) 支持(对象 / 数组可直接新增属性 / 元素)
顶层属性响应式 基本类型时天然支持;复杂类型时顶层属性变化支持 仅支持嵌套属性变化,顶层替换对象会丢失响应式
类型推导(TS) 自动推导,类型友好 需手动指定类型(复杂对象时),略繁琐

二、逐个差异点详解(配代码示例)

1. 适用数据类型:基本类型 vs 复杂类型

这是最核心的区别,直接决定了该选哪个 API。

ref:优先用于基本类型,兼容复杂类型
vue 复制代码
<script setup>
import { ref } from 'vue'

// ✅ 基本类型(推荐用法)
const name = ref('张三') // String
const age = ref(18) // Number
const isStudent = ref(true) // Boolean

// ✅ 复杂类型(兼容用法,内部自动转为reactive)
const user = ref({
  address: '北京',
  hobby: ['篮球']
})

// 修改数据:必须用 .value
name.value = '李四'
user.value.address = '上海' // 复杂类型时,.value 拿到的是reactive对象
user.value.hobby.push('游戏')
</script>
reactive:仅支持复杂类型,基本类型无效
vue 复制代码
<script setup>
import { reactive } from 'vue'

// ❌ 错误用法:基本类型用reactive,不会变成响应式
const age = reactive(18)
age = 20 // 毫无反应,不是响应式

// ✅ 正确用法:对象类型
const user = reactive({
  name: '张三',
  age: 18
})

// ✅ 正确用法:数组类型
const list = reactive(['苹果', '香蕉'])

// 修改数据:直接操作属性/元素
user.name = '李四'
list.push('橘子')
</script>

2. 访问方式:.value 的有无

这是新手最容易踩坑的点,记住一句话:ref需要.value,reactive不需要

ref:逻辑中必须加.value,模板中不用
vue 复制代码
<template>
  <!-- 模板中:直接用,无需.value -->
  <p>姓名:{{ name }}</p>
  <p>年龄:{{ age }}</p>
</template>

<script setup>
import { ref } from 'vue'

const name = ref('张三')
const age = ref(18)

// 逻辑中:必须加 .value 才能访问/修改
const updateUser = () => {
  name.value = '李四' // 正确:修改生效,视图更新
  age.value += 1 // 正确:年龄变成19
  // age = 19 // 错误:直接赋值会覆盖ref对象,丢失响应式
}
</script>
reactive:全程无需.value,符合直觉
vue 复制代码
<template>
  <!-- 模板中:直接用属性 -->
  <p>姓名:{{ user.name }}</p>
  <p>爱好:{{ list[0] }}</p>
</template>

<script setup>
import { reactive } from 'vue'

const user = reactive({ name: '张三' })
const list = reactive(['篮球'])

// 逻辑中:直接修改属性,无需.value
const updateData = () => {
  user.name = '李四' // 正确:视图更新
  list[0] = '游戏' // 正确:视图更新
}
</script>

3. 解构特性:是否需要toRefs

解构是日常开发常用操作,但reactive直接解构会丢失响应式,ref则不会。

ref:解构后仍保持响应式
vue 复制代码
<script setup>
import { ref } from 'vue'

const user = ref({
  name: '张三',
  age: 18
})

// 解构:直接解构ref对象的属性
const { name, age } = user.value // 注意:这里解构的是user.value(reactive对象)

// 修改:name和age是ref类型,需要.value
name.value = '李四' // 正确:user.name同步更新,响应式不丢失
age.value = 20 // 正确:视图更新
</script>
reactive:直接解构丢失响应式,需用toRefs
vue 复制代码
<script setup>
import { reactive, toRefs } from 'vue'

const user = reactive({
  name: '张三',
  age: 18
})

// ❌ 错误:直接解构,丢失响应式
const { name, age } = user
name = '李四' // 视图不更新,响应式失效

// ✅ 正确:用toRefs解构,保持响应式
const { name: newName, age: newAge } = toRefs(user)
newName.value = '李四' // 正确:需要.value,视图更新
newAge.value = 20 // 正确:user.age同步更新
</script>

4. 顶层属性响应式:替换对象的差异

当需要「替换整个对象」时,refreactive的表现不同:

ref:替换整个对象仍保持响应式
vue 复制代码
<script setup>
import { ref } from 'vue'

const user = ref({ name: '张三', age: 18 })

// 直接替换整个对象:响应式不丢失
user.value = { name: '李四', age: 20 } // 正确:视图更新
</script>
reactive:替换整个对象会丢失响应式
vue 复制代码
<script setup>
import { reactive } from 'vue'

const user = reactive({ name: '张三', age: 18 })

// ❌ 错误:替换整个对象,丢失响应式
user = { name: '李四', age: 20 } // 后续修改user.name,视图不更新

// ✅ 正确:不替换对象,只修改属性
user.name = '李四'
user.age = 20 // 响应式正常
</script>

三、实际开发中的选择原则(新手直接照做)

不用纠结,按以下优先级选择即可:

  1. 基本类型(String/Number/Boolean) :优先用 ref例:const count = ref(0)const username = ref('')
  2. 复杂类型(对象 / 数组) :优先用 reactive例:const form = reactive({ username: '', password: '' })const list = reactive([])
  3. 需要解构复杂类型reactive + toRefs例:const { username, password } = toRefs(form)
  4. 需要替换整个对象 :用 ref例:接口请求后需要替换整个数据对象时,data.value = res.data
  5. TypeScript 开发
    • 基本类型:ref(自动推导类型,无需额外配置)
    • 复杂类型:reactive + 接口定义(例:const user: User = reactive({...})

总结

  1. 核心区别ref 主打基本类型(需.value),reactive 主打复杂类型(无需.value);
  2. 关键坑点reactive 直接解构丢响应式(用toRefs解决),ref 修改必须加.value
  3. 选择原则 :基本类型用ref,复杂类型用reactive,解构用toRefs,替换对象用ref

记住这 3 点,开发中就不会再混淆两者的用法啦!如果遇到具体场景不确定选哪个,不妨把数据类型和需求(是否解构、是否替换对象)列出来,对照上面的原则就能快速判断~

相关推荐
HIT_Weston39 分钟前
49、【Ubuntu】【Gitlab】拉出内网 Web 服务:http.server 单/多线程分析(一)
前端·ubuntu·gitlab
天意__1 小时前
Flutter开发,scroll_to_index适配flutter_list_view
前端·flutter
吉星9527ABC1 小时前
表示离散量的echarts图型示例
前端·arcgis·echarts·离散量web展示
光影少年1 小时前
web3学习路线
前端·学习·前端框架·web3
克喵的水银蛇1 小时前
Flutter 状态管理:Provider 入门到实战(替代 setState)
前端·javascript·flutter
鹏多多1 小时前
flutter-使用url_launcher打开链接/应用/短信/邮件和评分跳转等
android·前端·flutter
刻刻帝的海角1 小时前
响应式数据可视化 Dashboard
开发语言·前端·javascript
小飞侠在吗1 小时前
vue3 中的 ref 和 reactive
前端·javascript·vue.js
0思必得01 小时前
[Web自动化] 开发者工具控制台(Console)面板
前端·javascript·python·自动化·web自动化·开发者工具