基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

ref 和 reactive 的区别是什么?

知识点图片

在 Vue 3 中,refreactive 都是用于创建响应式数据的核心 API,但它们在适用场景、访问方式和底层原理上有明显的区别。

以下是详细的对比总结:

1. 核心区别速览表

特性 ref reactive
支持的数据类型 所有类型 (基本类型 + 对象/数组) 仅引用类型 (对象、数组、Map、Set)
JS/TS 中访问 需要 .value (如 count.value) 直接访问 (如 state.count)
模板中访问 自动解包,不需要 .value 直接访问
重新赋值 可以直接替换整个对象 不能直接替换整个对象 (会丢失响应性)
解构 传递 ref 对象本身保持响应性 直接解构属性会丢失响应性 (需配合 toRefs)
底层原理 Object.defineProperty (基本类型) / reactive (对象类型) Proxy

2. 详细对比与代码示例

A. 数据类型支持

  • ref: 既可以定义基本数据类型(String, Number, Boolean),也可以定义对象。
  • reactive: 只能定义对象或数组。如果传入基本类型,Vue 会报警告且无法产生响应式。
javascript
// ref
const count = ref(0);          // OK
const user = ref({ name: 'A' }); // OK

// reactive
const state = reactive({ count: 0 }); // OK
const num = reactive(0);       // ❌ 错误用法,不起作用

B. 访问方式 (Script vs Template)

  • ref: 在 JS 代码中必须通过 .value 读写;在 <template> 中 Vue 会自动解包,不需要写 .value
  • reactive: 无论在哪里都是直接访问属性。
javascript
// Script
const count = ref(0);
console.log(count.value); // 必须加 .value

const state = reactive({ count: 0 });
console.log(state.count); // 直接访问

// Template
// <p>{{ count }}</p>       <!-- 不需要 .value -->
// <p>{{ state.count }}</p> <!-- 直接访问 -->

C. 对象的整体替换 (重要区别)

这是开发中最容易踩坑的地方。

  • ref: 可以直接替换 .value,响应性依然存在。
  • reactive: 不能直接将变量重新赋值为一个新对象,否则会切断与 Proxy 的联系,导致失去响应性。
javascript
// ref
const user = ref({ name: 'Alice' });
user.value = { name: 'Bob' }; // ✅ 依然是响应式的

// reactive
let state = reactive({ name: 'Alice' });
state = { name: 'Bob' }; // ❌ 变量 state 被重新赋值了,不再是响应式 Proxy 对象
// 正确做法:
Object.assign(state, { name: 'Bob' }); // ✅ 修改属性,保持响应性
// 或者包裹在 ref 中使用

D. 解构带来的问题

  • reactive: 如果对 reactive 对象进行解构,解构出的基本类型变量会失去响应性。
  • ref: ref 本身就是个包装对象,通常传递 ref 本身,不会去解构它的 .value
javascript
const state = reactive({ count: 0, name: 'Vue' });

// ❌ 失去响应性,count 只是一个普通的数字 0
let { count } = state; 
count++; // state.count 不会变

// ✅ 解决方案:使用 toRefs
const { count: countRef } = toRefs(state);
countRef.value++; // state.count 也会变

3. 底层原理简述

  1. reactive: 基于 ES6 的 Proxy 实现。它直接代理整个对象,拦截对象的读取和设置操作。
  2. ref:
    • 如果是基本类型:ref 创建一个包含 .value 属性的对象(RefImpl),通过 getset 拦截对 .value 的访问。
    • 如果是对象类型:ref 内部会自动调用 reactive 将其转换为 Proxy,然后放在 .value 中。

4. 最佳实践:该用哪一个?

这在社区中主要有两种观点,你可以根据团队习惯选择:

  • 观点一:一把梭全用 ref (官方文档目前的倾向)

    • 理由:统一心智模型,不用区分什么时候用 ref 什么时候用 reactive。虽然需要写 .value,但可以通过 IDE 插件(如 Volar)自动补全,且避免了 reactive 无法整体赋值和解构丢失响应性的问题。
  • 观点二:对象用 reactive,单值用 ref

    • 理由:将相关联的状态聚合在一起(类似 Vue 2 的 data),代码语义更清晰,且不需要到处写 .value

总结建议:
如果你是 Vue 3 新手,或者希望减少心智负担,建议优先使用 ref。当你需要处理逻辑紧密相关的一组状态,且确定不会对对象整体重新赋值时,可以使用 reactive

00:00
00:00