面试常考:简单来聊聊vue中ref和reactive的区别

26 阅读3分钟

1. 适用数据类型不同

ref :主要用于包装 基本数据类型 (String、Number、Boolean、Null、Undefined 等),也可以包装对象 / 数组(内部会自动转换为 reactive代理)。

reactive :仅用于包装 引用数据类型 (Object、Array 等),不能直接用于基本类型(否则会报警告且失去响应性)。

2. 访问 / 修改方式不同

ref :会将数据包装为一个 带有 value属性的响应式对象 ,因此访问 / 修改时需要通过 .value操作(模板中使用时会自动解包,无需 .value)。

import { ref } from 'vue';

const count = ref(0);

// 访问值
console.log(count.value); // 输出: 0

// 修改值
count.value++;
console.log(count.value); // 输出: 1

reactive :返回的是数据的 响应式代理对象 ,可以直接访问 / 修改属性,无需额外操作。

import { reactive } from 'vue';

const user = reactive({
 name: '赵六',
 age: 22
});

// 访问属性
console.log(user.name); // 输出: 赵六

// 修改属性
user.age = 23;
console.log(user.age); // 输出: 23

3. 解包特性不同

ref的自动解包 :

在模板中使用时,ref会自动解包(无需 .value)。

ref作为 reactive对象的属性时,会自动解包(访问时无需 .value

import { ref, reactive } from 'vue';

<!-- 在模板中,ref 会自动解包,无需 .value -->
<p>ref Count: {{ count }}</p> 

const count = ref(0);
const obj = reactive({ count }); // 将 ref 作为 reactive 对象的属性

// 访问时会自动解包,无需 .value
console.log(obj.count); // 输出: 0 

// 修改时也会自动解包
obj.count++;
console.log(count.value); // 输出: 1
console.log(obj.count);   // 输出: 1

reactive 无自动解包 :

访问属性时必须通过代理对象,直接解构会丢失响应性(需要用 toRefs处理)。

<!-- 在模板中,访问属性时必须通过代理对象-->
<p>reactive Count: {{ user.count }}</p>

const user = reactive({ name: '张三', age: 18 });
const { name } = user; // 解构后name失去响应性
name = '李四'; // 不会触发界面更新

// 正确做法:用toRefs转为ref对象
const { name, age } = toRefs(user);
name.value = '李四'; // 响应性保留

4. 替换数据的差异

ref :可以直接替换整个值(因为本质是对 value的修改),响应性不会丢失。

import { ref } from 'vue';

const user = ref({ name: '吴九', age: 35 });

console.log(user.value.name); // 输出: 吴九

// 直接替换整个对象
user.value = { name: '郑十', age: 40 };

console.log(user.value.name); // 输出: 郑十
// 响应性依然保持,界面会更新显示新的用户信息

reactive :不能直接替换整个对象(会切断与原始代理的联系,导致响应性丢失)。

import { reactive } from 'vue';

const user = reactive({ name: '小明', age: 16 });

// 错误示范:直接替换整个对象,会切断响应性连接
user = { name: '小红', age: 17 }; // 这会报错:Uncaught TypeError: Assignment to constant variable.
                                // 如果 user 是 let,虽然不报错,但原来的响应式代理丢失,所有依赖它的地方都不会更新

// 正确做法:修改属性而不是替换整个对象
user.name = '小红';
user.age = 17;

// 如果确实需要替换大量数据,可以考虑使用 Object.assign
const newUserData = { name: '小刚', age: 18 };
Object.assign(user, newUserData); // 这样可以保留响应性

5. 类型推断(TypeScript)

ref:类型推断更清晰,直接声明类型即可:

const count = ref<number>(0); // 明确 count 是 number 类型的 ref
count.value = 'hello'; // TypeScript 会报错(类型不匹配)

`

  1. TypeScript 环境下,需要清晰类型推断的场景。reactive`:类型推断依赖于初始值,若初始值为空对象,需手动声明接口:
interface State {
  count: number;
  name?: string;
}
const state = reactive<State>({ count: 0 }); // 明确类型
state.count = 'hello'; // TypeScript 报错

6. 使用场景建议

优先用ref

  • 存储基本类型数据(number、string、boolean 等)
  • 存储需要独立响应的单一数据(如表单输入值、计数器)
  • 数据可能被频繁赋值替换(如 count.value = 0
  • TypeScript 环境下,需要清晰类型推断的场景。

优先用 reactive

  • 存储复杂对象 / 数组(如用户信息、列表数据);
  • 数据具有多个关联属性(如 user 对象包含 nameageaddress);
  • 不需要替换整个对象,只需要修改内部属性的场景;
  • 希望避免频繁写 .value 的场景(在 reactive 中直接访问属性更简洁)。