在 Vue 里,provide / inject 是一套“跨层级传值”机制,常用来避免 props 一层层透传。
本文结合一个 demo 来说明这几个核心问题:
provide和inject分别是什么意思- “从最近的祖先取值”到底是什么意思
provide 了对应 key里的 key 是什么- 为什么同一个公共组件不会在不同区域“串状态”
Demo 链接
在线 Demo(新窗口打开):
查看 provide/inject 作用域示例
1. provide 和 inject 是什么
可以先把它们理解成一组“提供-领取”动作:
provide:祖先组件把一份值放进上下文,供后代使用inject:后代组件按 key 去取这份值
它们的价值是:当组件层级比较深时,不需要把同一份数据通过 props 一层层往下传。
2. 什么叫“从最近的祖先取值”
后代组件执行 inject 时,Vue 会沿组件树向上查找:
- 先看父组件有没有
provide对应 key - 没有就继续看父的父
- 一直往上,直到找到为止
- 找到第一个就停止
这个“第一个找到的祖先”就是“最近的祖先”。
所以 inject 的本质是“就近匹配”,不是“全局唯一”。
3. demo 中的 key 是什么
在这个 demo 里,你没有手写字符串 key,而是用了 @vueuse/core 的 createInjectionState:
export const [provideScopedCounter, useScopedCounterStore] =
createInjectionState(useScopedCounter)
这里的 key 由 createInjectionState 在内部生成并管理(通常是唯一的 Symbol)。
provideScopedCounter(...)用这个 key 执行 provideuseScopedCounterStore()用同一个 key 执行 inject
因为 key 是同一个,所以才能准确拿到对应的 store。
4. 为什么不会串
假设页面里有两个并列区域,每个区域外面各套了一个 ScopeProvider:
- 区域 A:
ScopeProvider(mode="product-detail") - 区域 B:
ScopeProvider(mode="quick-purchase")
这时每个区域内的 SharedPanel 都会向上找到“各自最近”的 ScopeProvider,因此两边互不影响,不会串状态。
但要注意:
- 同一个
ScopeProvider下的多个SharedPanel,会共享同一份 store(会联动) - 不同
ScopeProvider之间,store 相互隔离(不联动)
5. 嵌套 Provider 的情况
如果出现嵌套结构:
ScopeProvider(Outer)
├─ SharedPanel-A
└─ ScopeProvider(Inner)
└─ SharedPanel-B
结果是:
SharedPanel-A读取OuterSharedPanel-B读取Inner(因为 Inner 离它更近)
这就是“内层覆盖外层、就近生效”。
6. 在这个 demo 中,provide/inject 分别在哪执行
分开看会更清楚:
- 定义来源:
useScopedCounter.ts- 由
createInjectionState(useScopedCounter)生成并导出provideScopedCounter和useScopedCounterStore
- 由
- provide 实际执行:
ScopeProvider.vue- 在组件 setup 中调用
provideScopedCounter(props.initial, props.mode)
- 在组件 setup 中调用
- inject 实际执行:
SharedPanel.vue- 在组件 setup 中调用
useScopedCounterStore()读取 store
- 在组件 setup 中调用
所以可以记成一句话:函数在 useScopedCounter.ts 中定义,provide 在 ScopeProvider.vue 执行,inject 在 SharedPanel.vue 执行。
评论区