readonly 是 Vue 3 中创建深层只读代理的核心工具,拦截所有修改操作并在开发环境报警告;适用于共享配置、Pinia 只读 state、第三方响应式对象消费及函数参数声明只读场景。

readonly 是 Vue 3 响应式系统中用于创建只读代理的核心工具,它不会阻止你读取数据,但会拦截所有修改操作(如赋值、删除、添加属性等),并在开发环境下抛出警告——这是保护全局状态不被意外篡改的轻量又可靠的方式。
什么时候该用 readonly?
适合用于以下场景:
- 你暴露给组件或模块的共享状态(比如从
provide/inject传下去的配置对象) - 从 Pinia store 中取出、仅需展示不需修改的 state 片段
- 第三方库返回的响应式对象,你只想安全消费,不想承担被误改的风险
- 函数参数中接收响应式对象,明确声明“我只读,不负责改”
基础用法:包装 reactive 或 ref
它能作用于 reactive 创建的对象、ref 的 .value,甚至嵌套结构:
import { reactive, readonly } from 'vue'
const original = reactive({ count: 0, name: 'Alice' })
const safeState = readonly(original)
// ✅ 可读
console.log(safeState.count) // 0
// ❌ 修改会静默失败(生产环境),开发环境报 Warning
safeState.count = 1 // ⚠️ [Vue warn] Set operation on key "count" failed: target is readonly.
safeState.newField = 'x' // ⚠️ 同样警告
delete safeState.name // ⚠️ 也会被拦截
和 shallowReadonly 的区别
readonly 是**深层只读**:嵌套对象、数组内部也受保护;而 shallowReadonly 只冻结第一层,内部属性仍可改:
const deep = readonly({ user: { age: 25 } })
deep.user.age = 99 // ⚠️ 报错!整个树都只读
const shallow = shallowReadonly({ user: { age: 25 } })
shallow.user.age = 99 // ✅ 允许!只锁了 shallow 对象本身
多数保护全局状态的场景推荐用 readonly,除非你明确需要内部可变(比如只禁止替换整个对象,但允许更新子字段)。
从零开始编排 SpringBoot+Vue 全栈项目工作流,适用于新建全栈项目或完整脚手架请求,触发后依次调用 product‑spec‑builder(需求调研)等模块。
在 Pinia 和 provide/inject 中实战保护
例如在 store 中暴露只读状态:
// stores/user.js
export const useUserStore = defineStore('user', () => {
const state = reactive({ profile: {}, token: '' })
// 外部只能读,不能改 state 本身
const safeState = readonly(state)
return {
state, // 内部仍可改
safeState // 对外提供只读视图
}
})
在父组件中通过 provide 暴露时也建议包装:
// Parent.vue
import { provide, readonly } from 'vue'
import { useAppConfig } from '@/stores/config'
export default {
setup() {
const configStore = useAppConfig()
// 提供只读副本,子组件无法修改原始 store
provide('appConfig', readonly(configStore.$state))
}
}
不复杂但容易忽略——加一层 readonly 就能提前堵住很多“谁动了我的 state”的排查黑洞。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!









