IT评测·应用市场-qidao123.com技术社区

标题: Vue 3 reactive 和 ref 区别及 失去响应性问题 [打印本页]

作者: 滴水恩情    时间: 7 天前
标题: Vue 3 reactive 和 ref 区别及 失去响应性问题
在 Vue 3 中,reactive 和 ref 是实现响应式数据的两个核心 API,它们的设计目标和使用场景有所差别。以下是两者的详细对比:
1. 基本定义与核心功能

特性reactiveref作用创建对象范例的响应式署理(对象、数组、Map 等)创建一个响应式的引用,可用于基本范例或对象返回值原始对象的署理(Proxy)包含 value 属性的响应式对象(如 { value: xxx })响应式原理基于对象的属性劫持(Proxy + Reflect)基于 getter/setter 劫持,基本范例和对象统一处理解包机制无(直接访问属性)对象会主动解包为 reactive,基本范例需通过 .value 访问 2. 详细区别

(1) 实用数据范例


(2) 访问方式


(3) 响应式解包规则


(4) 重新赋值与响应式


(5) 组合式 API 中的使用


3. 典型使用场景

场景reactive 更合适ref 更合适定义对象 / 数组状态✅(reactive({ a: 1, b: 2 }))✅(ref({ a: 1, b: 2 }),主动解包)定义基本范例状态(如计数)❌(不支持)✅(ref(0))函数返回单个响应式值❌(需返回对象)✅(直接返回 ref(0),模板主动解包)解构响应式对象并保持响应式须要共同 toRefs(const { a, b } = toRefs(state))直接解构 ref(如 const { value } = count,但通常无需解构)动态创建响应式变量需手动构建对象直接使用 ref 包裹恣意值 4. 最佳实践

总结



明白两者的核心区别后,可根据具体场景选择最合适的 API,制止响应式失效问题(如之前代码中 reactive 对象直接赋值导致的更新失败,改用 ref 或正确更新属性即可解决)。

====================================================================

当须要整体更新 reactive 对象中的某个嵌套对象(例如从接口获取全新的用户数据并覆盖原有对象),同时又要保持响应式时,关键是不替换整个 reactive 对象的引用,而是通过以下两种方式更新其属性:
方法一:使用 Object.assign 合并新对象(推荐)

实用场景:


示例:

javascript
  1. import { reactive } from 'vue';
  2. // 初始化:state 是 reactive 对象,user 是嵌套对象
  3. const state = reactive({
  4.   user: { name: 'Alice', age: 30 }
  5. });
  6. // 假设从接口获取新用户数据(普通对象)
  7. const newUser = { name: 'Bob', age: 35, city: 'Beijing' };
  8. // 正确写法:将新属性合并到现有 reactive 对象中(保持代理引用)
  9. Object.assign(state.user, newUser);
  10. // 等价于:state.user = { ...state.user, ...newUser }; (但此写法错误,见下方说明)
复制代码
关键原理:


方法二:重新赋值时保持 reactive 署理(实用于全新对象)

实用场景:


方式 1:对新对象重新应用 reactive(不推荐,可能导致性能问题)

javascript
  1. // 错误:直接赋值新普通对象(失去响应式)
  2. state.user = newUser;
  3. // 正确:对新对象创建 reactive 代理后再赋值(保持响应式)
  4. state.user = reactive(newUser);
  5. // 注意:此时 state.user 是新的代理对象,原有代理会被丢弃,可能导致不必要的依赖追踪
复制代码
方式 2:用 ref 包裹对象(更优雅的解决方案)

假如嵌套对象须要频繁整体更新,发起将其定义为 ref,而非直接作为 reactive 的属性:

javascript
  1. import { reactive, ref } from 'vue';
  2. // 初始化:用 ref 包裹 user 对象(可整体更新)
  3. const state = reactive({
  4.   user: ref({ name: 'Alice', age: 30 }) // user 是 ref 对象
  5. });
  6. // 从接口获取新用户数据
  7. const newUser = { name: 'Bob', age: 35, city: 'Beijing' };
  8. // 正确写法:直接赋值给 ref 的 value(内部自动处理响应式)
  9. state.user.value = newUser;
  10. // 或先解包再赋值(模板中无需 .value,自动解包)
复制代码
原理:


方法三:解构新对象并逐个更新属性(实用于少量属性)

实用场景:


示例:

javascript
  1. // 假设 newUser 包含需要更新的属性
  2. state.user.name = newUser.name;
  3. state.user.age = newUser.age;
  4. state.user.city = newUser.city; // 直接更新每个属性,保持代理引用
复制代码
核心原则:制止丢失署理引用

无论接纳哪种方法,核心是确保 reactive 对象的引用不被替换

完整示例对比(错误 vs 正确)

错误写法(失去响应式):

javascript
  1. const state = reactive({ user: { name: 'Alice' } });
  2. // 错误:直接赋值新对象,user 失去 reactive 代理
  3. state.user = { name: 'Bob' };
  4. // 视图不会更新,因为 state.user 现在是普通对象
复制代码
正确写法 1(合并属性):

javascript
  1. const state = reactive({ user: { name: 'Alice' } });
  2. const newUser = { name: 'Bob', age: 30 };
  3. // 正确:合并新属性到现有代理对象
  4. Object.assign(state.user, newUser);
  5. // 或逐个赋值:state.user.name = 'Bob'; state.user.age = 30;
  6. // 视图会正确更新
复制代码
正确写法 2(用 ref 包裹对象):

javascript
  1. const state = reactive({ user: ref({ name: 'Alice' }) });
  2. const newUser = { name: 'Bob', age: 30 };
  3. // 正确:赋值给 ref 的 value,内部自动处理响应式
  4. state.user.value = newUser;
  5. // 视图会正确更新
复制代码
总结:推荐方案


通过以上方法,既能实现整体对象的更新,又能保持 Vue 3 的响应式特性,制止因引用丢失导致的视图差别步问题。



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com技术社区 (https://dis.qidao123.com/) Powered by Discuz! X3.4