当前位置:首页>排行榜>ref 和 reactive 怎么选?toRef、toRefs 一次讲明白

ref 和 reactive 怎么选?toRef、toRefs 一次讲明白

  • 更新时间 2026-10-31 20:06:02
ref 和 reactive 怎么选?toRef、toRefs 一次讲明白

同样是创建响应式数据,ref 和 reactive 到底什么时候用?为什么 reactive 解构之后就丢失响应式了?toRef、toRefs 又是用来解决什么问题的?

这一组 API 是 Vue3 日常开发里最高频、也最容易踩坑的地方,今天一次性梳理清楚,搭配示例,看完直接可以用到项目里。

一、先简单回顾:ref 和 reactive 基础用法

1. ref

ref() 既可以包裹基础类型(字符串、数字、布尔、null、undefined),也可以包裹对象、数组。它底层会把数据包装成一个 Ref 对象,内部值放在 .value 属性上。
<scriptsetup>import { ref } from 'vue'// 基础类型const count = ref(0)// 对象类型const user = ref({ name: '张三', age: 20 })// 读取、修改必须加 .valueconsole.log(count.value)count.value = 10user.value.name = '李四'</script><template>  <!-- 模板中自动解包,不用写 .value -->  <div>{{ count }}</div>  <div>{{ user.name }}</div></template>

2. reactive

reactive()只支持对象、数组这类引用类型,不支持基础类型。它内部就是用我们之前讲的 Proxy 做代理,返回原始对象的响应式代理,没有 .value。
<scriptsetup>import { reactive } from 'vue'const user = reactive({ name: '张三', age: 20 })const list = reactive([1,2,3])// 直接访问,不需要 .valueuser.name = '李四'</script><template>  <div>{{ user.name }}</div></template>
重点提醒:如果你直接给 reactive 变量整体重新赋值,响应式会直接丢失!

二、ref 和 reactive 怎么选?一张表看懂

对比项
ref
reactive
支持类型
基础类型 + 对象 / 数组
仅对象、数组(引用类型)
取值方式
js 中需要 .value,模板自动解包
直接访问,无 .value
重新赋值
可以直接替换 xxx.value,响应式保留
不能直接整体替换,只能修改属性
底层原理
基础类型靠类包装;对象内部自动调用 reactive
Proxy 代理对象
适用场景
单个简单变量、基础类型、需要整体替换的对象
结构化对象、表单对象、不需要整体替换的数据
简单选择口诀:
  • 单个数字、字符串、布尔值 → 优先 ref
  • 表单、多个属性组成的对象 → 优先 reactive
  • 不确定、后续可能直接替换整个对象 → 用 ref

三、致命问题:reactive 解构丢失响应式

看下面这段代码,很多人踩坑:
<scriptsetup>import { reactive } from 'vue'const user = reactive({ name: '张三', age: 20 })// 解构之后,name、age 变成普通变量,不再响应式const { name, age } = user</script><template>  <div>{{ name }}</div></template>
为什么解构之后就不响应了? 因为 reactive 返回的是 Proxy 代理对象,解构相当于把对象里的原始值拷贝出来,拿到的是普通值,脱离了 Proxy 的代理追踪。
解决方案:toRefs

四、toRefs:把 reactive 对象批量转成 ref

toRefs接收一个 reactive 响应式对象,把对象的每一个属性都转为 ref,之后再解构,保留响应式。
<scriptsetup>import { reactive, toRefs } from 'vue'const user = reactive({ name: '张三', age: 20 })// 批量转为 refconst { name, age } = toRefs(user)// js 里修改需要 .valuename.value = '李四'</script><template>  <div>{{ name }}</div></template>
适用场景:
  • 需要把 reactive 对象解构导出
  • 把对象属性传递给子组件、composable 函数返回
注意:toRefs 只会处理对象上已存在的属性,新增属性不会自动变成 ref。

五、toRef:单独给某一个属性创建 ref

toRef和 toRefs 不一样,它是针对单个属性创建 ref,而不是批量转换。 适合:只想抽离对象其中一个属性,而不是全部。
import { reactive, toRef } from 'vue'const user = reactive({ name: '张三', age: 20 })// 单独取出 nameconst nameRef = toRef(user, 'name')nameRef.value = '李四'
区分一句话:
  • toRefs:一整个对象全部属性转 ref
  • toRef:只拿对象里某一个属性转 ref

六、补充:浅层响应式 shallowRef /shallowReactive

很多业务场景不需要深度监听,比如后端返回超大列表、大对象,深度监听会有性能损耗,这时用浅层 API:
  • shallowRef:只监听.value的替换,不会对内部对象做响应式
  • shallowReactive:只监听对象第一层属性,深层属性不做响应式
import { shallowRef } from 'vue'const bigData = shallowRef({ list: [...] })// 有效:替换 value,触发更新bigData.value = { list: [] }// 无效:修改深层属性,不会触发视图更新bigData.value.list[0].name = 'xxx'

七、高频坑点汇总

  1. reactive 不能存基础类型,会报错,基础类型请用 ref
  2. reactive 不要直接整体赋值,会丢失响应式
  3. reactive 直接解构会丢失响应式,解构记得套 toRefs
  4. toRef /toRefs 创建的 ref,和原对象是引用关系,修改会同步影响原对象。
  5. toRefs 不能用于普通原始对象,普通对象转出来的 ref 不是响应式。

八、总结

  1. 基础类型 → ref;结构化对象表单 → reactive
  2. reactive 解构丢失响应式,toRefs 是标准解决方案
  3. toRef 取单个属性,toRefs 批量转换全部属性
  4. 超大对象、列表考虑 shallowRef /shallowReactive 优化性能
弄懂这一组,你在写<script setup> 的时候,绝大多数响应式报错都能自己定位了。

下一篇预告:Vue3 组件通信全梳理,props /emit/defineModel /provide/defineExpose 对比讲解,组件传值不再混乱。
如果你觉得文章有帮助,欢迎点赞、在看,一起打卡 Vue3 系列学习✨

随机文章