Vue3-Reactive

122 阅读3分钟

在 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

特性refreactive
数据类型任意类型对象/数组
访问方式.value直接访问
解构不适用解构会丢失响应性(需要toRefs)

参考文章

小满zs 学习Vue3 第七章(认识Reactive全家桶) xiaoman.blog.csdn.net/article/det…