前言
在 Vue 3 的 Composition API 中,ref 和 reactive 是定义响应式数据的两大基石。很多初学者常纠结于"什么时候该用哪个"。本文将从底层原理到实战场景,带你彻底理清两者的区别。
一、 核心概念对比
1. ref:全能型选手
-
定义 :主要用于定义基本类型(String, Number, Boolean 等),也可以定义引用类型。
-
本质 :通过对原始值进行包装,生成一个具有
.value属性的对象。对于引用类型,ref内部会自动调用reactive来处理。 -
访问控制:
- 在 JS 中必须通过
.value访问; - 在
<template>模板中,Vue 会自动解包,直接写变量名即可,无需加 .value。
- 在 JS 中必须通过
2. reactive:对象专家
-
定义 :专门用于定义引用类型(Object, Array, Map, Set)。
-
本质 :基于 ES6 Proxy 实现,直接代理整个对象。
-
访问控制 :像操作普通原生对象一样直接访问属性,无需
.value。注意: 传入基本类型会触发 Vue 警告且丢失响应式。
二、 深度差异对比
| 特性 | ref | reactive |
|---|---|---|
| 支持类型 | 基本类型 + 引用类型 | 仅限 引用类型 |
| JS 访问方式 | 需 .value |
直接访问属性 |
| 模板访问 | 自动解包,无需 .value |
直接访问 |
| 底层实现 | 包装基本类型,内部调用 reactive 处理引用类型 | 基于 Proxy 深度代理整个对象 |
| 替换整个对象 | 支持 (ref.value = 新对象/新数组) |
不支持(直接赋值会丢失代理,失去响应式) |
| 解构支持 | 直接解构丢失响应式(需 toRefs) |
直接解构丢失响应式(需 toRefs) |
三、 使用场景:我该怎么选?
推荐使用 ref 的场景:
-
基本类型数据:计数器、开关状态、输入框的值。
-
需要重置的数据 :例如从后端获取列表后,直接
list.value = res.data。 -
简单组件逻辑 :代码更清晰,
.value提醒这是一个响应式变量。
推荐使用 reactive 的场景:
-
复杂业务模型:包含多个相互关联属性的大对象(如用户信息、表单整组数据)。
-
追求原生感 :不希望在逻辑代码中到处看到
.value。 -
聚合数据:将一类变量聚合在一个对象中管理,减少变量声明。
四、 高频易错点
1. reactive 直接赋值整个对象会丢失响应式
JavaScript
let state = reactive({ count: 0 });
// ❌ 错误操作:这会导致 state 失去响应式,因为它变成了一个普通的普通对象
state = { count: 1 };
// ✅ 正确方案 A (ref):
const state = ref({ count: 0 });
state.value = { count: 1 };
// ✅ 正确方案 B (Object.assign):
Object.assign(state, { count: 1 });
2. 解构 reactive 数据丢失响应式
当你需要从一个响应式对象中提取属性并保持响应式时,必须使用 toRefs,否则会丢失响应式。
JavaScript
const props = reactive({ title: 'Vue3', author: 'Gemini' });
// 直接解构:const { title } = props; -> title 只是一个普通的字符串
const { title } = toRefs(props); // -> title 变成了一个 ref,保持响应式
3. Watch 监听的差异
-
监听
ref:默认只监听.value的变化,如果ref包裹的是对象,深度监听需要开启{ deep: true }。 -
监听
reactive:默认强制开启深度监听,且无法关闭。
📝 总结
- ref 是万金油,虽然多了个
.value,但胜在灵活且不易出错。 - reactive 适合组织复杂的对象数据,但要注意赋值和解构的陷阱。