在 Vue 3 中,reactive 是除了 ref 之外的另一种创建响应式数据的方式。
一般用来绑定复杂的数据类型,例如对象,数组
1.写法
reactive本质上是把传入的对象包裹成一个 ES6 Proxy
-
写法一:自动推断 (推荐用于简单的对象)
<script setup lang="ts">
import { reactive } from 'vue';
// TS 推断 user 为: { name: string; age: number; isActive: boolean }
const user = reactive({
name: 'YaeZed',
age: 18,
isActive: true
});
// ✅ 不需要 .value,直接像普通对象一样修改
user.age = 19;
</script>
-
写法二:显式接口定义 (推荐用于复杂业务)
在实际开发中,为了类型安全,我们通常会先定义接口(Interface)。
<script setup lang="ts">
import { reactive } from 'vue';
// 1. 定义接口
interface UserProfile {
id: number;
username: string;
tags: string[];
metadata?: { // 可选属性
lastLogin: string;
};
}
// 2. 使用泛型 <UserProfile> 或 类型注解
// 方式 A (泛型语法):
const profile = reactive<UserProfile>({
id: 1001,
username: 'admin',
tags: ['vue', 'ts']
});
// 方式 B (变量类型注解):
// const profile: UserProfile = reactive({ ... });
</script>
2.适用场景
reactive最适合用来处理 “一组高度相关联的状态” 。
-
场景一:表单数据 (Form State)
当你有许多表单字段时,使用
reactive将它们组合在一起比声明 10 个ref变量要整洁得多。
<script setup lang="ts">
import { reactive } from 'vue';
interface LoginForm {
username: string;
password: string;
rememberMe: boolean;
}
const formState = reactive<LoginForm>({
username: '',
password: '',
rememberMe: false
});
// 在 template 中绑定:v-model="formState.username"
</script>
-
场景二:由对象构成的配置项
<script setup lang="ts">
import { reactive } from 'vue';
const config = reactive({
theme: 'dark',
sidebar: {
isOpen: true,
width: 200
},
api: {
endpoint: 'https://api.example.com',
timeout: 5000
}
});
// reactive 是“深层响应式”的
// 修改深层属性也会触发视图更新
config.sidebar.width = 300;
</script>
3. reactive 的局限性
reactive只能接受对象、数组、Map、Set,不能用于基本类型。- 数组的异步赋值渲染问题
<template>
<div>{{ person.name }} is {{ person.age }}</div>
<button @click="addAge">点击增加年龄</button>
<div>
<ul>
<li v-for="person in personsObj.nameList">{{ person }}</li>
</ul>
<button @click.prevent="addPerson">添加人员</button>
</div>
</template>
<script setup lang="ts">
import { reactive, shallowReactive } from "vue";
// 1.reactive用来绑定复杂的数据类型 例如 对象 数组
const person = reactive({
name: "YaeZed",
age: 23,
});
// 2.可以直接修改person的属性,不用.value
const addAge = () => {
person.age++;
};
// 3.数组异步赋值渲染问题
let persons = reactive<string[]>([]);
let arr = ["YaeZed", "Tom", "Jerry"];
// const addPerson = () => {
// setTimeout(() => {
// // 这样赋值页面是不会变化的,因为reactive是proxy代理,直接赋值会破坏响应式
// persons = arr;
// console.log(persons); // 输出 ["YaeZed", "Tom", "Jerry"],但是模板并不会加载persons数组
// }, 1000);
// };
// 3.1 解决方案1,使用数组的方法push
// const addPerson = () => {
// setTimeout(() => {
// persons.push(...arr);
// }, 2000);
// };
// 3.2 解决方案2,包裹一层对象,数组设置为属性
type Person = {
nameList: string[];
};
const personsObj = reactive<Person>({
nameList: [],
});
const addPerson = () => {
setTimeout(() => {
personsObj.nameList = arr;
console.log(personsObj.nameList);
}, 2000);
};
</script>
<style scoped></style>
- 不能直接解构
const state = reactive({ count: 0, msg: 'hello' });
// 错误操作:解构
let { count } = state;
count++; // 变量 count 变了,但视图不会更新!state.count 也不会变!
解决方案:如果你必须解构,需要用
toRefs把它转换回一组ref。
import { toRefs } from 'vue';
const state = reactive({ count: 0, msg: 'hello' });
// 现在 count 和 msg 都是 ref 对象了
const { count, msg } = toRefs(state);
count.value++; // 视图更新,state.count 也会更新
- 不能整个替换对象
let state = reactive({ count: 0 });
// 这样赋值后,state 不再是响应式的,原来的响应式链接丢失了
state = reactive({ count: 1 });
// 正确做法:修改属性,或者使用 Object.assign
Object.assign(state, { count: 1 });
4.ref VS reactive
| 特性 | ref | reactive |
|---|---|---|
| 数据类型 | 任意类型 | 对象/数组 |
| 访问方式 | .value | 直接访问 |
| 解构 | 不适用 | 解构会丢失响应性(需要toRefs) |
参考文章
小满zs 学习Vue3 第七章(认识Reactive全家桶) xiaoman.blog.csdn.net/article/det…