一、两者是什么
- ref:把任意类型(
string/number/object/ DOM…)包成响应式对象,通过.value读写;模板里自动解包,不用写.value。 - reactive:专门给对象 / 数组用的深层响应式包装,直接访问属性即可,没有
.value。
import { ref, reactive } from "vue";
const count = ref(0);
count.value++; // 脚本里要 .value
console.log(count.value); // 1
const state = reactive({ name: "Tom", age: 18 });
state.age++; // 直接改属性
二、核心区别
| 维度 | ref | reactive |
|---|---|---|
| 支持类型 | 任意类型 | 仅对象 / 数组 |
| 访问方式 | .value | 直接 .属性 |
| 模板解包 | 自动解包 | 不需要解包 |
| 解构 | 解构后仍是响应(返回的是 ref) | 解构会丢失响应 |
| TS 推导 | 自动推导内部类型 | 类型来自原对象 |
| 替换整个对象 | xxx.value = newObj 仍响应 | 整体替换会丢响应 |
三、选型场景
- 用 ref: 基本类型(计数、开关、ID)、表单里的单值字段、DOM ref,以及组合式函数的返回值。
- 用 reactive: 一个多字段的表单对象或复杂状态对象(user、settings),用属性访问更自然。
- 组合式函数(composable)一律返回 ref: 调用方解构时不会丢失响应。
// composable 返回 ref,调用方解构也安全
export function useCounter() {
const count = ref(0);
const inc = () => count.value++;
return { count, inc }; // 解构 { count, inc } 仍是响应的
}
四、常见坑
- reactive 解构丢失响应 → 用
toRefs:
const state = reactive({ x: 1, y: 2 });
const { x, y } = toRefs(state); // 现在是 ref,仍响应
不解构直接 const { x } = state 会拿到普通数字,后续改 state.x 视图不更新。
ref 放进 reactive 会自动解包:
reactive({ count: ref(0) })访问时obj.count已是数字,不用.value。reactive 不能直接整体替换:
let state = reactive({ a: 1 });
state = { a: 2 }; // 变成普通对象,失去响应
// 用 Object.assign(state, { a: 2 }) 或改用 ref 包对象
- ref 数组 / Map 没坑;reactive 的数组整体替换也要注意:
arr = newArr会丢响应,改内容(push / splice)才安全。
五、相关阅读
- 《Vue3新特性及和Vue2的部分差异性(持续更新汇中)》(/2023/04/12043.html) —— Vue3 新特性全貌与 Vue2 差异
- 《详解Vue3中shallowRef和shallowReactive的使用》(/2026/05/26164.html) —— 浅响应式场景与性能优化