Vue3 中的 provide 和 inject 详解:实现跨组件通信
一、provide 和 inject 概述
在 Vue3 中,provide
和 inject
是一对用于实现跨层级组件通信的 API,它们解决了 props 需要逐层传递的繁琐问题。
1.1 基本概念
provide (提供):在祖先组件中提供数据
inject (注入):在任意后代组件中注入这些数据
1.2 与 props 的对比
特性 | props | provide/inject |
---|---|---|
传递方向 | 父 → 子 | 祖先 → 任意后代 |
层级限制 | 必须逐层传递 | 可跨任意层级 |
适用场景 | 直接父子组件通信 | 主题/配置/全局数据 |
响应性 | 默认支持 | 需要额外处理 |
二、基本使用方法
2.1 选项式 API 写法
javascript
// 祖先组件
export default {provide: {theme: 'dark'}
}// 后代组件
export default {inject: ['theme'],created() {console.log(this.theme) // 输出 'dark'}
}
2.2 组合式 API 写法(推荐)
html
<!-- 祖先组件 -->
<script setup>
import { provide } from 'vue'// 提供静态数据
provide('theme', 'dark')// 提供响应式数据
const count = ref(0)
provide('count', count)
</script><!-- 后代组件 -->
<script setup>
import { inject } from 'vue'// 注入数据
const theme = inject('theme')
const count = inject('count')// 设置默认值
const size = inject('size', 'default') // 如果没有提供 size,则使用 'default'
</script>
三、响应式处理
3.1 保持响应性
html
<script setup>
import { provide, ref } from 'vue'const count = ref(0)
provide('count', count)// 在提供者中修改
function increment() {count.value++
}
</script><!-- 后代组件 -->
<script setup>
import { inject } from 'vue'const count = inject('count')// 注入的值会自动保持响应性
watch(count, (newVal) => {console.log('count changed:', newVal)
})
</script>
3.2 提供修改方法(推荐模式)
html
<script setup>
import { provide, ref } from 'vue'const count = ref(0)// 同时提供值和修改方法
provide('count', {count,increment: () => count.value++
})
</script><!-- 后代组件 -->
<script setup>
const { count, increment } = inject('count')
</script>
四、高级用法
4.1 使用 Symbol 作为 key
避免命名冲突:
javascript
// keys.js
export const THEME_KEY = Symbol('theme')// 提供者
import { THEME_KEY } from './keys'
provide(THEME_KEY, 'dark')// 注入者
const theme = inject(THEME_KEY)
4.2 应用层 provide
在应用级别提供全局数据:
javascript
// main.js
import { createApp } from 'vue'
import App from './App.vue'const app = createApp(App)
app.provide('appVersion', '1.0.0')
app.mount('#app')
4.3 结合 TypeScript 使用
typescript
interface User {id: numbername: string
}// 提供者
const user = ref<User>({ id: 1, name: 'John' })
provide<User>('user', user)// 注入者
const user = inject<User>('user')
五、最佳实践
避免滥用:只在确实需要跨多层组件通信时使用
命名规范:使用有意义的键名或 Symbol
文档说明:为提供的属性添加注释说明
响应式处理:确保正确处理响应式数据
类型安全:在 TypeScript 项目中定义清晰接口
六、典型应用场景
主题切换功能
国际化实现
用户身份信息共享
全局配置参数
复杂表单中的状态共享
七、完整示例
7.1 主题切换实现
html
<!-- ThemeProvider.vue -->
<script setup>
import { provide, ref } from 'vue'const theme = ref('light')
const toggleTheme = () => {theme.value = theme.value === 'light' ? 'dark' : 'light'
}provide('theme', {theme,toggleTheme
})
</script><template><slot />
</template><!-- App.vue -->
<template><ThemeProvider><NavBar /><Content /></ThemeProvider>
</template><!-- NavBar.vue -->
<script setup>
const { theme, toggleTheme } = inject('theme')
</script><template><button @click="toggleTheme">当前主题: {{ theme }}</button>
</template>
八、注意事项
不是响应式的:如果直接提供基本类型值,注入的值不会是响应式的
避免直接修改:除非明确设计如此,否则避免在注入组件中直接修改注入的值
组件封装性:过度使用会破坏组件独立性,使组件更难复用
九、总结
provide
和 inject
是 Vue3 中强大的跨组件通信工具,特别适合解决"prop 逐级透传"问题。正确使用它们可以:
简化深层嵌套组件间的通信
实现全局状态管理(轻量级替代 Vuex/Pinia)
创建可复用的上下文组件