【Vue】将响应式对象转为非响应式对象
一、使用 toRaw
函数获取原始对象
作用:获取由 reactive()
或 ref()
创建的代理对象的原始数据副本,解除响应式追踪。 实现:
import { reactive, toRaw } from 'vue';
const proxy = reactive({ name: '张三' });
const rawData = toRaw(proxy); // 获取原始对象
rawData.name = '李四'; // 修改不会触发视图更新
console.log(proxy.name); // '李四'(数据同步变化,但无响应式触发)
特点:
- 引用关系保留:
rawData
是原响应式对象的引用,修改会影响原对象数据,但不会触发视图更新。 - 适用场景:序列化数据(如提交给后端)、传递到非响应式第三方库时使用。
二、通过 markRaw
永久标记非响应式
作用:标记对象或属性,使其即使被嵌套在响应式对象中也不会转换为 Proxy。 实现:
import { reactive, markRaw } from 'vue';
const obj = markRaw({ price: 100 }); // 标记为非响应式
const state = reactive({product: obj // 嵌套后仍为非响应式
});
state.product.price = 200; // 修改有效,但不会触发响应式更新
特点:
- 深度阻断:被标记对象的所有层级属性均不会成为响应式。
- 适用场景:静态配置数据、复杂类实例或大型列表性能优化。
三、直接解构(Vue3.4以及之前)或赋值导致响应性丢失
原理:响应式依赖通过属性访问触发追踪,直接解构或赋值会破坏引用链。 示例:
const proxy = reactive({ count: 0 });
let { count } = proxy; // 解构基本类型
count++; // 不影响原对象
const localObj = proxy.obj; // 解构引用类型
localObj.name = 'test'; // 修改会影响原对象,但无响应式更新
注意:
- 深层次对象:若解构的是对象,修改其属性仍会影响原数据,但不会触发视图更新(需配合
toRaw
或markRaw
彻底解除)。
四、替换整个响应式对象
场景:通过重新赋值整个对象来“重置”响应式状态。 实现:
let state = reactive({ list: [] });
// 直接替换为新对象(需注意引用丢失问题)
state = { list: [1, 2, 3] }; // 新对象无响应式
局限性:
- 响应式覆盖问题:需重新调用
reactive()
或ref()
才能使新对象恢复响应式。
方法对比与选型建议
方法 | 响应式影响范围 | 数据引用关系 | 适用场景 |
---|---|---|---|
toRaw | 解除单次代理 | 保持引用 | 临时数据导出、避免副作用 |
markRaw | 永久阻断嵌套响应式 | 保持引用 | 静态数据、性能优化 |
解构/赋值 | 局部属性失去响应式 | 可能断开 | 简单数据操作 |
扩展:源码视角下的响应式解除
从 Vue3 源码看,reactive()
基于 Proxy 实现,而 ref()
通过 RefImpl
包装对象。toRaw
通过访问代理对象的 __v_raw
属性获取原始数据,而 markRaw
会给对象添加 __v_skip
标记,跳过响应式转换。因此,合理使用这些 API 能精准控制响应式边界。