ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Vue 中 ref 和 reactive 有什么区别?该用哪个?

Vue 中 ref 和 reactive 有什么区别?该用哪个?

Vue 中 ref 和 reactive 有什么区别?该用哪个?

一句话总结:ref 适合「基本类型 + 需要重新赋值」的场景,reactive 适合「对象/数组 + 不需要整体替换」的场景。记不住?基本类型用 ref,对象用 reactive,需要整体替换的用 ref 包对象,就这么简单!


正文目录

  1. ref 和 reactive 的核心区别是什么?
  2. 5 大高频场景 & 代码示例
  3. 万能兜底:ref vs reactive 决策表
  4. 预防 checklist(不再踩坑)
  5. 一句话总结

一、ref 和 reactive 的核心区别是什么?

问题背景:Vue 3 的响应式系统基于Proxy,提供了refreactive两种创建响应式数据的 API,很多开发者分不清什么时候用哪个。

对比项refreactive
支持类型任意类型(基本类型 + 对象)仅对象/数组
访问方式.value(模板中自动解包)直接访问属性
重新赋值✅ 直接xxx.value = newVal❌ 丢失响应式
解构需要配合toRefs直接解构会丢失响应式
模板中使用自动解包,无需.value直接使用
深层响应✅ 默认深层✅ 默认深层
import{ref,reactive}from'vue'// ✅ ref:基本类型constcount=ref(0)count.value++// JS 中需要 .value// 模板中:{{ count }} 自动解包,不需要 .value// ✅ reactive:对象conststate=reactive({name:'Claw',age:3})state.age=4// 直接修改属性// ❌ reactive 整体替换 → 丢失响应式state=reactive({name:'New',age:5})// 不报错但响应式断了

二、5 大高频场景 & 代码示例

场景 1:计数器 / 开关 → 用 ref

基本类型,需要频繁修改,用 ref 最直观。

// ✅ 正确:基本类型用 refconstisLoading=ref(false)constcount=ref(0)constmessage=ref('hello')functiontoggle(){isLoading.value=!isLoading.value}
// ❌ 错误:reactive 不支持基本类型constcount=reactive(0)// Vue 警告:value should be an object

场景 2:表单对象 → 用 reactive

多个关联字段组成一个对象,不需要整体替换,用 reactive 更自然。

<script setup> import { reactive } from 'vue' // ✅ 表单对象用 reactive const form = reactive({ username: '', password: '', remember: false }) function submit() { console.log(form.username, form.password) } function reset() { // ⚠️ 逐字段重置,不能整体替换 form.username = '' form.password = '' form.remember = false } </script>
// ❌ 错误:整体替换丢失响应式functionreset(){form={username:'',password:'',remember:false}// 响应式断了!}

场景 3:需要整体替换的对象 → 用 ref

从接口拿数据后整体赋值,用 ref 包对象更安全。

// ✅ 正确:接口数据用 ref,方便整体替换constuserInfo=ref(null)asyncfunctionfetchUser(){constres=awaitapi.getUser()userInfo.value=res.data// ✅ 整体替换没问题}
// ❌ 麻烦:reactive 要逐字段赋值constuserInfo=reactive({})asyncfunctionfetchUser(){constres=awaitapi.getUser()Object.assign(userInfo,res.data)// 能用但啰嗦// 或者逐字段:userInfo.name = res.data.name ...}

场景 4:解构响应式对象 → 用 toRefs 配合 reactive

reactive 解构后会丢失响应式,必须用toRefs包装。

<script setup> import { reactive, toRefs } from 'vue' const state = reactive({ name: 'Claw', age: 3 }) // ❌ 直接解构:丢失响应式 // const { name, age } = state // ✅ toRefs 解构:保持响应式 const { name, age } = toRefs(state) </script>
// ref 则不需要 toRefsconstcount=ref(0)// 模板中直接用 {{ count }},无需解构

场景 5:watch 监听 → ref 和 reactive 行为不同
import{ref,reactive,watch}from'vue'// ✅ ref:watch 默认监听 .value 的变化constcount=ref(0)watch(count,(newVal,oldVal)=>{console.log(`${oldVal}${newVal}`)})// ✅ reactive:watch 默认深层监听conststate=reactive({count:0})watch(state,(newVal)=>{console.log('state changed',newVal.count)})// ⚠️ watch reactive 的某个属性 → 需要用 getterwatch(()=>state.count,(newVal,oldVal)=>{console.log(`${oldVal}${newVal}`)})// ⚠️ ref 包对象 → 默认不深层监听,需要 deepconstobj=ref({count:0})watch(obj,(newVal)=>{console.log(newVal)// obj.value = {} 时触发},{deep:true})// 修改 obj.value.count 需要 deep

三、万能兜底:ref vs reactive 决策表

场景用什么原因
基本类型(string/number/boolean)refreactive 不支持基本类型
独立的对象/数组,不需要整体替换reactive访问自然,不需要.value
接口返回的数据,需要整体替换ref可以直接xxx.value = newData
表单对象reactive字段关联,逐字段操作
需要解构使用的对象reactive+toRefsref 不需要解构
定时器/ DOM 引用ref存任意值,不触发渲染
组合式函数返回值ref调用方可以解构后保持响应式

万能决策流程

是基本类型吗? → 是 → 用 ref → 否 → 需要整体替换吗? → 是 → 用 ref(包对象) → 否 → 用 reactive
// 万能模板import{ref,reactive}from'vue'// 基本类型constloading=ref(false)consttotal=ref(0)// 对象 - 不需要整体替换constform=reactive({name:'',email:''})// 对象 - 需要整体替换(接口数据)constlist=ref([])asyncfunctionloadData(){list.value=awaitapi.getList()}

四、预防 checklist(不再踩坑)

  • 基本类型必须用ref,不要用reactive
  • reactive对象不要整体替换,用Object.assign或逐字段赋值
  • 接口数据整体赋值 → 用ref包对象
  • 解构reactive→ 必须用toRefstoRef
  • 模板中ref自动解包,JS 中必须.value
  • watch监听reactive属性 → 用 getter 函数() => state.xxx
  • watch监听ref对象的深层变化 → 加{ deep: true }
  • 组合式函数返回值优先用ref,方便调用方解构

五、一句话总结

基本类型用 ref,固定结构对象用 reactive,需要整体替换的对象用 ref 包裹——记不住就看要不要重新赋值,要重新赋值就用 ref。


最后问候亲爱的朋友们,并邀请你们阅读我的全新著作

📚 《Vue.js 3企业级项目开发实战(微课视频版》

返回列表