vue父子组件通信(二)祖先调用provide / inject(2)vue3
·
概况
provide / inject(跨层级,但也适用父子)。适合深层嵌套,避免 props 逐层传递:
<!-- 父组件(祖先) -->
<script setup>
import { provide, ref } from 'vue'
const theme = ref('dark')
provide('theme', theme)
</script>
<!-- 子/孙组件 -->
<script setup>
import { inject } from 'vue'
const theme = inject('theme')
</script>
一、通信
| 对比项 | props/emit | provide/inject |
|---|---|---|
| 传递层级 | 只能父子 | 任意层级 |
| 代码量 | 层级深时繁琐 | 简洁 |
| 数据来源 | 明确 | 隐式,不易追踪 |
| 适用场景 | 直接父子通信 | 跨层级、全局配置、主题、权限等 |
1、祖父 → 孙子传数据
写法
GrandParent
└── Parent
└── Child ← 想直接拿到 GrandParent 的数据
<!-- GrandParent.vue(提供数据) -->
<script setup>
import { provide, ref } from 'vue'
const username = ref('zs')
provide('username', username) // key: 'username', value: ref
</script>
<template>
<Parent />
</template>
<!-- Parent.vue(中间层,不需要做任何事) -->
<template>
<Child />
</template>
<!-- Child.vue(注入数据) -->
<script setup>
import { inject } from 'vue'
const username = inject('username') // 拿到的是响应式 ref
</script>
<template>
<div>Hello, {{ username }}</div> <!-- 显示:Hello, zs-->
</template>
当然也可以省略Parent
GrandParent(provide)
└── Child(inject) ← 直接注入,不需要中间层
<!-- GrandParent.vue -->
<script setup>
import { provide, ref } from 'vue'
const username = ref('zs')
provide('username', username)
</script>
<template>
<Child /> <!-- 直接用 Child,没有中间层 -->
</template>
<!-- Child.vue -->
<script setup>
import { inject } from 'vue'
const username = inject('username')
</script>
设置默认值(inject 找不到时的兜底)
<script setup>
import { inject } from 'vue'
// 第二个参数是默认值
const username = inject('username', 'Guest')
const config = inject('config', () => ({ size: 14 }), true) // 第三个参数 true 表示默认值是工厂函数
</script>
只读保护(防止子组件乱改数据)
<!-- 祖先组件 -->
<script setup>
import { provide, ref, readonly } from 'vue'
const count = ref(0)
provide('count', readonly(count)) // 子组件只能读
provide('increment', () => count.value++) // 只暴露方法来修改
</script>
2、子修改祖先的数据
provide 不仅可以传数据,还可以传方法,让子组件调用来修改祖先的数据:
<!-- GrandParent.vue -->
<script setup>
import { provide, ref } from 'vue'
const theme = ref('light')
function toggleTheme() {
theme.value = theme.value === 'light' ? 'dark' : 'light'
}
provide('theme', theme) // 提供数据
provide('toggleTheme', toggleTheme) // 提供修改方法
</script>
<template>
<div :class="theme">
<Child />
</div>
</template>
<!-- Child.vue -->
<script setup>
import { inject } from 'vue'
const theme = inject('theme')
const toggleTheme = inject('toggleTheme')
</script>
<template>
<div>
<p>当前主题:{{ theme }}</p>
<button @click="toggleTheme">切换主题</button>
</div>
</template>
二、语法
inject 是 Vue 中的一个 依赖注入(Dependency Injection)机制,用于:
跨层级组件传值(祖先组件 → 后代组件)
它通常和 provide 配对使用。
作用是:
避免一层一层通过
props往下传(prop drilling)。
1. 为什么需要 inject?
正常父子通信:
App
└── Parent
└── Child
└── GrandChild
如果 App 的数据要给 GrandChild:
通常要:
App → Parent → Child → GrandChild
一级一级:
<Parent :theme="theme" />
再:
<Child :theme="theme" />
再:
<GrandChild :theme="theme" />
很麻烦。
于是 Vue 提供:
provide / inject
让后代组件直接拿。
2. 基本用法
祖先组件 provide
<script>
export default {
provide() {
return {
theme: 'dark'
}
}
}
</script>
后代组件 inject
<script>
export default {
inject: ['theme'],
created() {
console.log(this.theme)
}
}
</script>
输出:
dark
即使中间隔了很多层组件,也能拿到。
3. 更完整例子
App.vue
<script>
export default {
provide() {
return {
username: 'Tom'
}
}
}
</script>
<template>
<Parent />
</template>
Parent.vue
<template>
<Child />
</template>
不需要传 props。
Child.vue
<script>
export default {
inject: ['username']
}
</script>
<template>
<div>{{ username }}</div>
</template>
页面:
Tom
4. inject 和 props 的区别
| 对比 | props | inject |
|---|---|---|
| 通信方式 | 父 → 子 | 祖先 → 任意后代 |
| 是否逐层传递 | 是 | 不需要 |
| 是否明确 | 明确 | 隐式 |
| 推荐场景 | 普通组件通信 | 全局/跨层级共享 |
5. 常见场景
① UI 组件库
比如:
<Form>
<FormItem>
<Input />
</FormItem>
</Form>
Input 要知道:
- label 宽度
- 校验状态
- form 配置
难道一级级 props?
不会。
通常:
provide('formContext')
inject('formContext')
Element Plus、Ant Design Vue 都大量使用。
② 全局配置
例如:
主题:
theme
locale
size
permission
③ 插件开发
Vue Router、Pinia、i18n 底层也用了类似机制。
比如:
app.provide(routerKey, router)
然后:
inject(routerKey)
拿 router 实例。
6. 响应式问题(重要)
很多人踩坑。
错误理解
以为:
provide() {
return {
count: 0
}
}
改了会响应更新。
不一定。
因为普通值不是响应式共享。
正确方式(Vue3)
传 ref/reactive
import { ref } from 'vue'
const count = ref(0)
provide('count', count)
子组件:
const count = inject('count')
直接响应式更新。
7. Vue2 写法
provide() {
return {
user: this.user
}
}
但:
this.user = 'new'
可能不更新。
Vue2 对 provide 响应式支持比较弱。
通常要:
provide() {
return {
state: this
}
}
然后:
inject: ['state']
访问:
state.user
8. 一句话理解
props 是:
父组件显式传参
inject 是:
后代组件向祖先“拿依赖”
有点像 Java/Spring:
@Autowired
UserService userService;
祖先:
@Bean
后代:
@Inject
所以很多人把 Vue 的 provide/inject 理解成:
轻量级依赖注入(DI)机制。
更多推荐

所有评论(0)