模式切换
组件间通信:provide/inject
provide
和 inject
是 Vue 提供的一对 API,用于实现跨层级组件通信,特别适合解决深层嵌套组件之间的数据传递问题,避免了 prop 逐层传递的繁琐。
基本概念
作用
provide
:在祖先组件中提供数据inject
:在后代组件中注入这些数据
与 props 的区别
特性 | props | provide/inject |
---|---|---|
方向 | 父 → 子 | 祖先 → 任意后代 |
层级 | 只能直接传递给子组件 | 可跨任意层级 |
响应性 | 自动保持 | 需要手动处理 |
适用场景 | 直接父子通信 | 深层嵌套组件通信 |
基本用法
javascript
// 祖先组件
import { provide } from 'vue'
export default {
setup() {
provide('siteName', 'My Awesome Site')
}
}
// 后代组件
import { inject } from 'vue'
export default {
setup() {
const siteName = inject('siteName')
return { siteName }
}
}
响应式数据传递
保持响应性
javascript
// 祖先组件
import { provide, ref } from 'vue'
export default {
setup() {
const count = ref(0)
// 提供响应式数据
provide('count', count)
return { count }
}
}
// 后代组件
import { inject } from 'vue'
export default {
setup() {
const count = inject('count')
function increment() {
count.value++ // 修改会影响祖先组件
}
return { count, increment }
}
}
提供方法
javascript
// 祖先组件
provide('updateCount', (newVal) => {
count.value = newVal
})
// 后代组件
const updateCount = inject('updateCount')
updateCount(10) // 调用祖先提供的方法
类型安全(TypeScript)
基础类型定义
typescript
// 祖先组件
provide<number>('count', 0)
// 后代组件
const count = inject<number>('count')
带默认值
typescript
const count = inject<number>('count', 0) // 第二个参数是默认值
复杂类型
typescript
interface User {
name: string
age: number
}
provide<User>('user', { name: 'Alice', age: 25 })
const user = inject<User>('user')
最佳实践
使用 Symbol 作为 key
避免命名冲突:
javascript
// keys.js
export const COUNT_KEY = Symbol()
// 祖先组件
import { COUNT_KEY } from './keys'
provide(COUNT_KEY, count)
// 后代组件
import { COUNT_KEY } from './keys'
const count = inject(COUNT_KEY)
封装为组合式函数
javascript
// useCounterProvider.js
import { provide, ref } from 'vue'
export function useCounterProvider(initialValue = 0) {
const count = ref(initialValue)
provide('count', count)
function increment() {
count.value++
}
return { count, increment }
}
// useCounterInject.js
import { inject } from 'vue'
export function useCounterInject() {
const count = inject('count')
return { count }
}
合理的使用场景
- 主题/样式配置:全局主题色、字体大小等
- 用户认证信息:当前用户数据
- 全局状态:比 Vuex/Pinia 更轻量的解决方案
- 复杂表单:跨多个嵌套表单组件共享状态
注意事项
- 不要滥用:简单父子通信优先用 props
- 响应性维护:确保提供的值是响应式的(ref/reactive)
- 明确依赖:在文档中清晰说明提供的值
- 命名规范:使用有意义的名称或 Symbol
完整示例
主题切换功能
vue
// 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)
provide('toggleTheme', toggleTheme)
</script>
// DeepChildComponent.vue
<script setup>
import { inject } from 'vue'
const theme = inject('theme')
const toggleTheme = inject('toggleTheme')
</script>
<template>
<button @click="toggleTheme">
当前主题: {{ theme }}
</button>
</template>
总结
provide/inject
是 Vue 中强大的跨组件通信方案:
- 优点:解决深层嵌套组件通信问题,避免 prop 逐层传递
- 注意:需要手动维护响应性,不适合简单场景
- 最佳实践:配合 TypeScript 和 Symbol 使用,封装为组合式函数