vue 3 中实现部分字段只读有四种方式:一是用 readonly 包裹整个 reactive 对象,使全部字段只读;二是拆分核心字段(readonly)与可变字段(reactive)并组合;三是用 computed 模拟只读字段并在 set 中拦截;四是自定义 hook 封装 proxy 拦截逻辑。

在 Vue 3 中,reactive 创建的对象默认是**深度可响应且可修改**的。如果你希望其中某些字段“逻辑上只读”(比如不允许组件或业务代码意外修改),readonly 本身不能直接作用于部分字段——它只能对整个对象或嵌套结构做浅/深只读封装。但你可以通过组合策略实现“仅核心字段只读”的效果。
用 readonly 包裹整个 reactive 对象(最简方式)
这是最直接的做法:先定义完整 reactive 对象,再用 readonly 封装,使其所有属性都不可被修改(开发时会报 warning,运行时静默失败)。
- 适用场景:对象整体语义上就是不可变的(如配置项、API 响应缓存)
-
写法示例:
const state = reactive({<br> id: 1,<br> name: 'Alice',<br> updatedAt: new Date()<br>})<br><br>const readOnlyState = readonly(state) // 所有字段都只读 -
限制:无法单独放开某个非核心字段(如允许修改
name)
拆分 reactive 和 readonly:核心字段用 readonly + 非核心字段用独立 ref 或 reactive
更灵活的方式是把“必须只读的核心字段”和“允许修改的字段”分开管理,避免耦合:
- 将
id、createdAt等不变字段抽成独立的readonly(ref(...))或直接用readonly包裹小对象 - 把可变字段(如
name、status)放在另一个reactive对象中 - 模板中统一使用,逻辑上仍是“一个实体”
示例:
const core = readonly(reactive({<br> id: 1,<br> createdAt: new Date()<br>}))<br><br>const mutable = reactive({<br> name: 'Alice',<br> status: 'active'<br>})<br><br>// 组合使用<br>const user = { ...toRefs(core), ...toRefs(mutable) }
用 computed 模拟“只读字段”,配合 setter 报错或忽略
如果必须保持单个 reactive 对象结构,又想控制某些字段不可赋值,可用 computed + set 拦截:
- 把核心字段改为
computed({ get, set }),set中抛错或直接 return - 注意:这不会阻止响应式依赖更新,但能阻断非法写入
示例(只读 id):
const state = reactive({<br> _id: 1,<br> name: 'Alice'<br>})<br><br>const user = {<br> get id() { return state._id },<br> set id(_) {<br> console.warn('id is read-only')<br> },<br> get name() { return state.name },<br> set name(v) { state.name = v }<br>}
进阶:自定义 hook 封装“部分只读 reactive”
可抽象为可复用逻辑,例如 usePartialReadonly:
- 接收原始 reactive 对象和只读字段名数组
- 返回一个代理对象,对指定字段拦截
set,其余透传 - ⚠️ 注意:Proxy 无法在 template 中直接响应式工作,需配合
toRefs或shallowRef使用
这类方案适合中大型项目统一管控,但增加了复杂度,建议优先考虑前两种更 Vue-native 的方式。











