provide 本身不提供数据保护,需配合 readonly 实现“只读下发”:父组件在 provide 时用 readonly 包裹 ref/reactive/computed,使子组件可读不可写,防止意外修改破坏响应式一致性。

Provide/Inject 本身不提供数据保护能力,readonly 是 Vue 响应式系统中用于防止意外修改的工具,二者配合使用可实现“只读下发”——即父组件通过 provide 提供数据,子组件能读取但不能直接修改原始响应式状态。
为什么需要 readonly 配合 provide?
默认情况下,provide 下发的是响应式对象(如 ref、reactive)的引用。子组件若解构或直接赋值,可能无意中修改原始数据,破坏响应式一致性与状态管理边界。
例如:
- 父组件 provide({ count: ref(0) });
- 子组件 const { count } = inject('xxx'),再执行 count.value++ → 原始值被改;
- 若下发的是 reactive({ a: 1 }),子组件 obj.a = 2 同样生效。
用 readonly 包裹后,任何试图修改属性的操作在开发模式下会触发警告(生产环境静默),从语义和实践上约束消费方行为。
正确配合方式:provide 时包裹 readonly
关键是在 provide 侧对要下发的数据显式调用 readonly,而非在 inject 侧处理。
将静态 Vue 页面(index.vue)改造为动态页面(Custom.vue);稿源仅限 index.vue、其引用的 CSS 与资源路径,禁止参考仓库内其他文件。Invoke when user wants to transform a static Vue page into a dynamic data-driven page with Element UI components.
立即学习“前端免费学习笔记(深入)”;
- 对 ref:用
readonly(ref()) - 对 reactive 对象:用
readonly(reactive({})) - 对 computed:通常已只读,但若需确保,也可再包一层
readonly(computed(...)) - 避免解构后丢失响应性或只读性:推荐注入后直接使用对象访问,而非提前解构
实际写法示例
父组件(Provider):
import { provide, readonly, ref, reactive } from 'vue'
<p>export default {
setup() {
const count = ref(0)
const userInfo = reactive({ name: 'Alice', role: 'user' })</p><pre class="brush:php;toolbar:false;">// ✅ 正确:下发只读版本
provide('sharedState', {
count: readonly(count),
userInfo: readonly(userInfo),
theme: readonly(computed(() => 'dark'))
})
return () => <Child />} }
子组件(Consumer):
import { inject } from 'vue'
<p>export default {
setup() {
const state = inject('sharedState')</p><pre class="brush:php;toolbar:false;">// ✅ 安全读取
console.log(state.count.value) // 0
console.log(state.userInfo.name) // 'Alice'
// ❌ 开发环境下报错:Set operation on key 'value' failed: target is readonly.
// state.count.value++
// ❌ 同样报错
// state.userInfo.name = 'Bob'
return () => <div>Theme: {state.theme}</div>} }
注意事项与边界
-
readonly是浅层只读:仅顶层属性不可赋值,若值是嵌套对象,其深层属性仍可修改(除非整个嵌套结构也都用readonly递归包裹) - 如需深层只读,可用
shallowReadonly(仅顶层)或手动包装深层结构,或改用不可变数据结构(如 Immer + produce) - 函数类型数据不受
readonly影响,若函数内会修改外部响应式状态,仍需靠设计约束(如只暴露纯函数或事件回调) - 服务类对象(含方法)若需只读,建议将状态与方法分离,或在方法内部做防护

















