readonly用于创建只读响应式代理,不阻止原对象修改但禁止对副本写操作,开发时警告、生产时静默失败,支持嵌套响应式对象的响应性同步,非深度冻结。

在 Vue 3 的 Composition API 中,readonly 用于创建一个**只读的响应式代理**,它不会阻止原始对象被修改,但能防止对副本的写操作(如赋值、删除属性、添加新属性),同时保留响应式更新能力。它和 ref 或 reactive 配合使用,常用于向子组件或外部逻辑暴露“只读视图”。
readonly 的基本用法:包装 reactive 对象
如果你已有通过 reactive() 创建的响应式对象,直接用 readonly() 包一层即可:
示例:
import { reactive, readonly } from 'vue'
const original = reactive({ count: 0, name: 'Alice' })
const readOnlyCopy = readonly(original)
// ✅ 可读取,且响应式更新会同步
console.log(readOnlyCopy.count) // 0
original.count++ // 触发更新
console.log(readOnlyCopy.count) // 1 ← 自动更新
// ❌ 尝试修改只读副本会静默失败(开发模式下有警告)
readOnlyCopy.count = 100 // 控制台警告:Set operation on key "count" failed: target is readonly.
readOnlyCopy.newField = 'test' // 同样失败
delete readOnlyCopy.name // 失败
readonly 不是深冻结,嵌套对象仍可变
readonly 是浅层只读 —— 它只保护顶层属性不可写,**嵌套的响应式对象(如 reactive 或 ref)内部仍可修改**:
- 如果嵌套的是
reactive对象,其内部属性仍可被修改,且变化会反映在只读副本中; - 如果嵌套的是普通对象(非 reactive),则它本身不具备响应性,也不会被
readonly深度保护; - 若需真正不可变,应配合
Object.freeze()或使用不可变数据结构(注意:Object.freeze()会破坏响应性,慎用)。
与 computed + toRefs 或 toRef 的区别
有时你会看到用 computed(() => ({ ...state })) 模拟只读,但这种方式:
- 每次访问都新建对象,失去响应式引用一致性(比如在
v-for中可能触发不必要重渲染); - 无法保留原始响应式对象的原型链和方法;
- 不是真正的代理保护,只是返回只读快照。
而 readonly() 返回的是同一响应式对象的只读代理,性能更好、语义更清晰、支持类型推导(TypeScript 友好)。
常见使用场景
-
向子组件提供只读 props:父组件传
readonly(props)给子组件,避免子组件意外修改; -
封装 store 状态暴露接口:在自定义 hook 中返回
readonly(state),隐藏可变逻辑; -
组合多个 reactive 对象后统一只读导出:如
readonly({...a, ...b})(注意:展开后失去响应性,应改用reactive({...toRefs(a), ...toRefs(b)})再readonly)。


















