readonly 深层只读,递归冻结所有嵌套层级;shallowreadonly 仅冻结顶层属性,嵌套内容仍可变。选型取决于不变性边界:整体不可改用 readonly,顶层固定而内部需响应更新则用 shallowreadonly。

readonly 和 shallowReadonly 都用于创建只读的响应式对象,但它们的“只读深度”不同:前者递归冻结整个响应式结构,后者仅冻结第一层属性。
readonly:深层只读,彻底禁止修改
对响应式对象或普通对象调用 readonly() 后,返回的对象及其所有嵌套层级(包括数组、对象、Map、Set 等)都变为只读。任何试图修改属性、添加/删除字段、或调用 mutating 方法(如 push、set)的操作都会在开发环境下触发警告,并在严格模式下静默失败(实际不生效)。
- 适用于需要完全保护数据结构的场景,比如从 API 获取后不再变更的配置项、权限列表、静态字典
- 它不会影响原始对象——
readonly()返回的是一个新代理,原对象仍可修改(除非本身也是响应式且被其他逻辑约束) - 注意:只读代理仍保持响应性,即当其源是响应式对象时,视图仍能响应内部变化(但你无法主动改它)
shallowReadonly:浅层只读,嵌套对象仍可变
shallowReadonly() 只阻止对顶层属性的写入和删除,嵌套的普通对象、数组等内部结构不受限制。它适合那些“顶层结构固定、但内部内容可能动态更新”的场景。
使用 `@ainative/vue-sdk` 为 Vue 3 应用添加 AI 聊天功能。适用于以下场景:(1) 安装 `@ainative/vue-sdk`,(2) 在 Vue 组件中使用 `useChat` 组合式函数,(3) 提供……
- 常见于表格列配置(
columns数组不可增删,但每列的width或visible可响应式更新) - 性能开销更小,尤其对深层嵌套对象,避免了递归代理的初始化成本
- 若嵌套的是响应式对象(如
ref或reactive),其内部变更依然会触发视图更新
怎么选?看你的不变性边界在哪
关键不是“哪个更好”,而是“你希望哪一层不能被改”。如果整个数据树都不该被意外修改,用 readonly;如果只是防止误删/重赋值顶层字段,而允许子对象自由响应变化,shallowReadonly 更轻量也更灵活。
- 不要用
shallowReadonly去保护本该整体冻结的配置数据,否则深层修改仍可能发生 - 也不要对简单扁平对象盲目用
readonly,除非真有深层嵌套且需防护 - 两者都支持解构,但解构出的属性仍是只读的(ES6 解构本身不破坏只读性)
配合 computed 使用更自然
很多时候,只读数据来自计算派生。直接在 computed 中返回 readonly 或 shallowReadonly 结果,既明确语义,又避免多余代理:
const filteredList = computed(() => readonly(filter(items.value, condition)))const tableState = computed(() => shallowReadonly({ columns, loading }))
这样既能保证消费端无法篡改,又让逻辑更聚焦于“意图”而非“防御性封装”。










