Vue3 中 `reactive` 与 `readonly` 的深度解析

208 阅读2分钟

Vue3 中 reactivereadonly 的深度解析

一、核心功能对比

特性reactivereadonlyshallowReadonly
响应式类型深层响应式深层只读浅层只读(仅第一层属性不可变)
数据修改允许修改所有层级属性禁止修改,触发警告(开发环境)禁止修改第一层属性,深层可变
适用场景可变状态管理(如组件内部数据)保护全局配置、传递不可变数据保护对象顶层结构
嵌套对象处理自动递归代理递归代理为只读仅代理第一层,深层保留原始状态

二、使用场景与示例

  1. reactive 基础用法
import { reactive } from 'vue';
 
// 创建深层响应式对象 
const state = reactive({
  count: 0,
  user: { name: 'Alice' }
});
 
// 修改属性 
state.count = 1;          // 触发响应式更新 
state.user.name = 'Bob';  // 深层属性修改同样触发更新 
 
 
  1. readonly 保护数据
import { reactive, readonly } from 'vue';
 
const source = reactive({ config: { theme: 'dark' } });
 
// 创建只读代理 
const readOnlyConfig = readonly(source);
 
// 尝试修改会触发警告(开发环境)
readOnlyConfig.config.theme = 'light';  // 控制台警告:Set operation on key "theme" failed 
 
 
  1. shallowReadonly 限制表层修改
import { shallowReadonly } from 'vue';
 
const state = shallowReadonly({
  settings: { fontSize: 16 }
});
 
state.settings = {};       // 禁止修改(第一层属性)
state.settings.fontSize = 14; // 允许修改(深层属性)
 
 

三、源码级特性说明

  1. 代理机制
    • reactive 使用 Proxy 递归代理对象的所有层级。
    • readonlyProxy 会拦截 set/deleteProperty 操作并阻止修改 [6]
  2. 性能优化
    • 深层只读 (readonly) 比浅层 (shallowReadonly) 有更高的初始化开销,适合稳定数据。
    • 浅层只读适用于动态层级结构(如配置对象)[4]
  3. 类型兼容性
    // 类型推断示例 
    interface State {
     id: number;
     metadata: { tags: string[] };
    }
    
    const state: Readonly<State> = readonly(reactive<State>({ 
     id: 1, 
     metadata: { tags: ['vue'] }
    }));
    
    

---

四、常见问题与解决 

1. 误修改只读数据导致警告
现象:控制台提示 `Set operation on key "xxx" failed`。  
解决:  
- 检查数据来源是否为 `readonly` 包装对象。
- 使用 `isReadonly()` 进行运行时验证:
  ```javascript 
 import { isReadonly } from 'vue';
 if (!isReadonly(data)) {
   // 安全修改逻辑 
 }
 

  1. 深层对象意外变更 场景:shallowReadonly 的深层属性被意外修改。
    方案:
  • 改用 readonly 或手动冻结深层对象:
    const state = shallowReadonly({
     nested: Object.freeze({ value: 42 })
    });
    
    

---

五、最佳实践 
1. 状态分层设计  
  - 使用 `reactive` 管理组件内部可变状态。
  - 通过 `readonly` 向子组件传递数据,避免副作用 [[2]](https://blog.csdn.net/weixin_47450807/article/details/122889417)。

2. 性能敏感场景  
  - 对高频更新的数据避免深层只读,优先使用 `shallowReactive` 或 `shallowReadonly`[[5]](https://www.jb51.net/javascript/320439nu3.htm)。

3. TypeScript 增强  
  - 结合 `Readonly<T>` 类型强化只读约束:
     ```typescript 
    import { reactive, readonly } from 'vue';
    type Config = Readonly<{ apiUrl: string }>;
    const config: Config = readonly(reactive({ apiUrl: 'https://api.example.com' }));
     ``` 

通过合理运用 `reactive` 和 `readonly`,可以在保证数据安全性的同时,充分利用 Vue3 响应式系统的能力。