readonly无法直接防止子组件修改inject注入的数据,因其不递归冻结深层属性;需结合响应式机制与设计约束,如用readonly包裹ref/reactive、提供computed或getter、分离状态与操作、避免注入原始响应式对象。

使用 readonly 无法直接防止子组件修改通过 inject 注入的数据,因为 readonly 只作用于响应式代理本身,不递归冻结深层属性。真正有效的防护需结合响应式机制与设计约束。
用 readonly 包裹响应式数据(基础防护)
若注入的是 ref 或 reactive 对象,应在 provide 侧显式包裹 readonly:
- 对
ref:用readonly(ref()),返回只读的 ref,赋值会静默失败(开发模式下有警告) - 对
reactive:用readonly(reactive({})),禁止修改属性、添加/删除字段,但嵌套对象仍可被改(除非也用readonly逐层包裹) - 注意:
readonly不等于Object.freeze,它基于 Proxy 实现,仅拦截 set/delete 操作,不阻止内部方法调用或非响应式属性修改
提供计算属性或只读 getter(更安全)
避免直接暴露可变响应式对象,转而注入纯函数或计算值:
- 用
computed(() => xxx)提供派生状态,子组件只能读取,无法反向修改源数据 - 用
{ get: () => state.value }形式注入只读访问器,隐藏原始 ref - 示例:注入
{ count: computed(() => store.count) },子组件拿到的是只读计算 ref,无法触发.value =赋值
分离状态与操作,限制修改入口
将“数据”和“变更逻辑”解耦,只注入操作函数而非响应式对象本身:
- provide 侧暴露
{ getCount: () => state.count, updateCount: (n) => {...} } - 子组件只能调用方法修改,无法直接操作响应式字段
- 配合 TypeScript 类型定义,把注入值声明为
Readonly<{ count: number }>,提升开发时提示与约束
避免注入原始响应式对象(根本规避)
最彻底的方式是不注入 ref/reactive,而注入普通对象或结构化数据快照:
- 用
toRaw()+JSON.parse(JSON.stringify())创建不可变副本(适合低频、小数据) - 在 inject 侧用
shallowRef存储只读快照,配合手动更新通知(如事件总线) - 注意:这会丢失响应性,需权衡是否需要实时同步
核心原则是:readonly 是辅助手段,不是银弹;真正的防护来自架构设计——控制数据流向、明确读写边界、用类型和 API 约束代替运行时强制。

















