shallowreadonly用于创建浅层只读代理,仅冻结顶层属性,内部嵌套对象或数组仍可变;适用于需保护顶层引用、保留内部可变性且避免深层冻结开销的场景。

shallowReadonly 用于创建一个浅层只读的响应式代理,它只冻结第一层属性,内部嵌套的对象或数组仍保持可变(非只读、非响应式增强)。
什么时候该用 shallowReadonly?
当你需要防止外部直接修改顶层属性,但又希望保留内部对象/数组的可变性(比如手动触发更新、配合非响应式逻辑处理),同时避免 readonly 带来的深层递归冻结开销时,就适合用它。
典型场景包括:
- 将 props 或从 API 获取的初始数据包装为“不可被意外覆盖顶层引用”的状态
- 配合
shallowRef或普通对象使用,避免过度响应式化 - 性能敏感场景下,避免对大型嵌套结构做深度只读转换
基本用法:创建浅层只读代理
直接传入一个普通对象或响应式对象即可:
示例:import { shallowReadonly, reactive } from 'vue'
const state = reactive({
count: 1,
user: {
name: 'Alice',
profile: { age: 30 }
},
list: [1, 2, 3]
})
const readonlyState = shallowReadonly(state)
// ✅ 允许(内部对象仍可修改)
readonlyState.user.name = 'Bob' // OK
readonlyState.list.push(4) // OK
readonlyState.user.profile.age = 31 // OK
// ❌ 报错(顶层属性赋值被拦截)
readonlyState.count = 2 // TypeError: Set operation on key 'count' failed
readonlyState.user = {} // TypeError: Set operation on key 'user' failed
readonlyState.list = [] // TypeError: Set operation on key 'list' failed
和 readonly 的关键区别
两者都返回只读代理,但冻结深度不同:
-
readonly(obj):递归地将所有嵌套属性设为只读(深层冻结) -
shallowReadonly(obj):仅冻结对象自身属性的写入,不深入嵌套
这意味着:shallowReadonly 下的嵌套对象修改不会触发视图更新(因为它们本身不是响应式),除非你额外用 reactive 包裹它们。
注意事项与常见陷阱
- 它不改变原始对象的响应性 —— 如果传入的是
reactive对象,原始对象依然可写;shallowReadonly只是返回一个新代理 - 对
ref使用需注意:应传入ref.value(即解包后对象),或用shallowReadonly(toRaw(ref))避免 ref 被代理包裹 - 不能阻止通过原型链或 Object.defineProperty 等方式绕过代理的修改(但 Vue 场景中极少遇到)
- 在 TypeScript 中,类型会推导为
DeepReadonly<t></t>的浅层等价 —— 实际是ShallowReadonly<t></t>,需确保类型定义已导入(Vue 3.3+ 自动支持)











