完整教程:Vue3 响应式 API:ref 和 reactive 怎么选?用 1 个例子讲透
上一篇我们对比了 Vue2 和 Vue3 的 API 写法,但有个关键问题没说透:为什么有时候我们在 Vue3 里改了变量,页面却不更新?比如这样写:
<script setup>
let count = 0;
function addCount() {
count += 1; // 改了变量,但页面不更新!
}
</script>
计数:{{ count }}
这不是 BUG,而是因为 Vue3 需要用 “响应式 API”(ref/reactive)包裹变量,才能让页面 “感知” 到变量变化。这篇就用最直白的案例,讲清 ref 和 reactive 的用法、区别和选择逻辑,从此告别 “变量改了页面不动” 的坑。
先搞懂:什么是 “响应式”?
用大白话讲:变量变了,页面自动跟着变,不用手动刷新,这就是响应式。
比如我们要做一个 “计数器”,点击按钮让 count 从 0 变成 1,页面能立刻显示 1—— 要实现这个效果,就必须用 ref 或 reactive 把 count 变成 “响应式变量”。
Vue3 的响应式原理和 Vue2 不同(Vue2 用Object.defineProperty,Vue3 用Proxy),但不用深扒原理,记住 “用对 API” 就行,重点看下面的案例。
第一部分:ref—— 适合给 “基础类型” 做响应式
基础类型指的是:数字(number)、字符串(string)、布尔值(boolean),比如 count=0、name="张三"、isShow=true。
1.1 ref 的用法:3 步搞定
步骤 1:引入 ref(必须!)
Vue3 的响应式 API 需要手动引入,在<script setup>开头加一行:
<script setup>
// 从vue里引入ref
import { ref } from "vue";
</script>
步骤 2:用 ref 包裹变量,定义响应式数据
把普通变量用ref()包起来,比如定义响应式的 count:
<script setup>
import { ref } from "vue";
// 用ref包裹基础类型,变成响应式变量
const count = ref(0); // 初始值0
</script>
步骤 3:修改值要加.value,模板里不用
- JS 里修改:ref 包裹的变量,在<script>里改值必须加.value(这是 ref 的核心特点,也是新手常忘的坑)
- 模板里使用:在<template>里直接用变量名,不用加.value
完整计数器案例:
计数:{{ count }}
<script setup>
import { ref } from "vue";
const count = ref(0); // 响应式变量
function addCount() {
// JS里改值必须加.value!
count.value += 1;
}
</script>
保存后点击按钮,页面会实时更新计数 —— 这就是响应式的效果!
1.2 ref 也能包裹对象(但不推荐)
虽然 ref 主要用在基础类型,但也能包裹对象(比如用户信息),只是修改属性时同样要加.value:
<script setup>
import { ref } from "vue";
// ref包裹对象
const user = ref({
name: "张三",
age: 20
});
function changeUser() {
// 改属性:user.value.属性名
user.value.name = "李四";
user.value.age = 22;
}
</script>
但这样写有点麻烦(要多写.value),所以对象类型更推荐用下面的 reactive。
第二部分:reactive—— 适合给 “复杂类型” 做响应式
复杂类型指的是:对象(object)、数组(array),比如 user={name:""}、list=[1,2,3]—— 这些包含多个子属性 / 元素的数据,用 reactive 更顺手。
2.1 reactive 的用法:3 步搞定
步骤 1:引入 reactive(必须!)
和 ref 一样,先从 vue 里引入:
<script setup>
import { reactive } from "vue";
</script>
步骤 2:用 reactive 包裹对象 / 数组,定义响应式数据
比如定义一个 “用户信息” 对象,包含 name 和 age 两个属性:
<script setup>
import { reactive } from "vue";
// 用reactive包裹对象,变成响应式数据
const user = reactive({
name: "张三",
age: 20
});
</script>
步骤 3:修改值不用加.value,直接改属性
- JS 里修改:reactive 包裹的对象,直接通过 “对象。属性” 修改,不用加.value
- 模板里使用:和 ref 一样,直接用 “对象。属性”
完整用户信息修改案例:
姓名:{{ user.name }}
年龄:{{ user.age }}
<script setup>
import { reactive } from "vue";
const user = reactive({
name: "张三",
age: 20
});
function changeUser() {
// 直接改属性,不用加.value,比ref更简洁
user.name = "李四";
user.age = 22;
}
</script>
点击按钮,页面会立刻更新用户信息 —— 对比 ref 包裹对象的写法,reactive 少了.value,更清爽。
2.2 reactive 的避坑点:不能直接 “重新赋值整个对象”
这是 reactive 最容易踩的坑!如果直接给 reactive 对象赋值,会丢失响应式,比如这样写:
<script setup>
import { reactive } from "vue";
const user = reactive({ name: "张三", age: 20 });
function changeUser() {
// 错误写法:直接赋值整个对象,响应式会丢失!
user = { name: "李四", age: 22 };
// 改完后页面不更新,因为user变成了普通对象
}
</script>
正确写法:要么改属性(像之前那样),要么用Object.assign合并对象:
function changeUser() {
// 正确写法1:改单个属性
user.name = "李四";
user.age = 22;
// 正确写法2:用Object.assign合并(适合多属性修改)
Object.assign(user, {
name: "李四",
age: 22
});
}
第三部分:关键对比 ——ref 和 reactive 怎么选?
用 “用户信息 + 计数器” 的综合案例,分别用 ref 和 reactive 实现,直观对比两者的差异:
案例需求:
- 显示用户姓名、年龄
- 显示计数器(初始 0)
- 一个按钮:修改用户信息 + 计数器加 1
用 ref 实现(基础类型 + 对象):
姓名:{{ user.name }}
年龄:{{ user.age }}
计数:{{ count }}
<script setup>
import { ref } from "vue";
// 基础类型用ref
const count = ref(0);
// 对象也用ref(但要多写.value)
const user = ref({ name: "张三", age: 20 });
function handleClick() {
count.value += 1; // 加.value
user.value.name = "李四"; // 加.value
user.value.age = 22; // 加.value
}
</script>
用 reactive 实现(对象包裹所有数据):
姓名:{{ data.user.name }}
年龄:{{ data.user.age }}
计数:{{ data.count }}
<script setup>
import { reactive } from "vue";
// 用一个reactive对象包裹所有数据(count+user)
const data = reactive({
count: 0,
user: { name: "张三", age: 20 }
});
function handleClick() {
data.count += 1; // 不用加.value
data.user.name = "李四"; // 不用加.value
data.user.age = 22; // 不用加.value
}
</script>
选择口诀(10 秒判断):
- 基础类型(number/string/boolean):优先用 ref(reactive 不支持单独的基础类型)
- 复杂类型(object/array):优先用 reactive(比 ref 少写.value,更简洁)
- 不确定或混合类型:用 ref 包裹对象(虽然麻烦点,但不容易丢响应式)
- 追求代码统一:全用 ref(比如项目里既有基础类型又有对象,全用 ref 不用记两种规则)
第四部分:实战避坑总结
- ref 的.value只在 JS 里加:模板里不用加,加了会报错
- reactive 不能直接赋值整个对象:只能改属性或用Object.assign
- 数组用 reactive 更方便:比如const list = reactive([1,2,3]),修改时直接list.push(4),不用加.value
- 解构 reactive 会丢失响应式:比如const { name } = user,改name页面不更新,解决方法:用toRefs(下一篇讲)
总结:这篇你学会了什么?
- 响应式的核心:变量变,页面自动变,需要用 ref/reactive 包裹
- ref 用法:基础类型优先,JS 改值加.value,模板不用
- reactive 用法:复杂类型优先,直接改属性,不用加.value
- 选择口诀:基础用 ref,对象用 reactive,不确定全用 ref
下一篇我们会讲 “解构响应式数据” 的问题 —— 比如用const { name } = user后改 name 页面不更新怎么办?还有toRefs和toRef的用法,彻底解决响应式数据的 “灵活使用” 问题!如果这篇的案例你有疑问,或者想测试其他场景,欢迎在评论区留言~

浙公网安备 33010602011771号