provide / inject 与依赖注入
约 771 字大约 3 分钟
布欧-Lewyon
2026-05-15
首页 › Vue › 组件与通信 › provide / inject 与依赖注入
当嵌套层级很深时,用 props 逐层传递(prop drilling)非常痛苦。provide / inject 允许祖先组件向其所有后代"注入"依赖。
基础用法
Provide
<!-- App.vue(祖先组件) -->
<script>
export default {
data: () => ({
theme: 'dark',
user: { name: 'Admin', role: 'admin' }
}),
provide() {
return {
theme: this.theme,
user: this.user
}
}
}
</script>Inject
<!-- DeepChild.vue(任意层级的后代) -->
<script>
export default {
inject: ['theme', 'user'], // 注入
created() {
console.log(this.theme) // 'dark'
console.log(this.user.name) // 'Admin'
}
}
</script>
<template>
<div :class="`theme-${theme}`">
<p>当前用户: {{ user.name }}</p>
</div>
</template>与 Props 的对比
| Props | provide / inject | |
|---|---|---|
| 方向 | 父 → 子 | 祖先 → 任意后代 |
| 显式性 | 显式声明,容易追踪 | 隐式注入,来源不直观 |
| 中间层 | 每层都要传 | 跳过中间层直接注入 |
| 适合场景 | 组件间明确的数据传递 | 跨多层级的全局性数据(主题、用户、国际化) |
响应式注入
默认情况下,provide 的值不是响应式的。需要传响应式对象或 ref:
<script>
import { ref, provide } from 'vue'
export default {
data: () => ({
theme: ref('dark') // ref 对象传递引用
}),
provide() {
return {
theme: this.theme, // 传 ref 本身,而非值
updateTheme: (val) => { this.theme.value = val }
}
}
}
</script>后代中:
<script>
export default {
inject: ['theme', 'updateTheme'],
methods: {
toggleTheme() {
this.updateTheme(this.theme.value === 'dark' ? 'light' : 'dark')
}
}
}
</script>provide 注入 Symbol 避免命名冲突
在大项目中,注入名容易冲突。建议用 Symbol:
// symbols.js
export const THEME_KEY = Symbol('theme')
export const USER_KEY = Symbol('user')<script>
import { THEME_KEY, USER_KEY } from './symbols'
export default {
provide() {
return {
[THEME_KEY]: this.theme,
[USER_KEY]: this.user
}
}
}
</script>后代:
<script>
import { THEME_KEY } from './symbols'
export default {
inject: {
localTheme: { from: THEME_KEY }, // 重命名注入
user: { from: USER_KEY, default: { name: '访客' } } // 提供默认值
}
}
</script>注入选项详解
<script>
export default {
inject: {
// 简单注入
theme: 'theme',
// 带别名
localUser: { from: 'user' },
// 带默认值(注入不存在时使用)
config: { from: 'appConfig', default: { debug: false } },
// 默认值工厂函数
items: { from: 'items', default: () => [] }
}
}
</script>Composition API 中的 provide/inject
<!-- 祖先组件 -->
<script setup>
import { ref, provide } from 'vue'
const theme = ref('dark')
provide('theme', theme)
function toggleTheme() {
theme.value = theme.value === 'dark' ? 'light' : 'dark'
}
provide('toggleTheme', toggleTheme)
</script><!-- 后代组件 -->
<script setup>
import { inject } from 'vue'
const theme = inject('theme', ref('light')) // 第二个参数为默认值
const toggleTheme = inject('toggleTheme')
</script>
<template>
<div :class="`theme-${theme}`">
<button @click="toggleTheme">切换主题</button>
</div>
</template>实战:全局主题注入
<!-- ThemeProvider.vue -->
<script setup>
import { ref, provide, readonly } from 'vue'
const theme = ref('light')
const primaryColor = ref('#1890ff')
function setTheme(t) { theme.value = t }
function setPrimaryColor(c) { primaryColor.value = c }
// 提供只读数据 + 修改方法,防止后代意外修改
provide('theme', readonly(theme))
provide('primaryColor', readonly(primaryColor))
provide('setTheme', setTheme)
provide('setPrimaryColor', setPrimaryColor)
</script>
<template>
<div :style="{ '--primary': primaryColor }" class="theme-wrapper">
<slot />
</div>
</template>小结
| 概念 | 要点 |
|---|---|
| provide/inject | 祖先→后代任意层级注入,解决 prop drilling |
| 响应式 | 需要传 ref/reactive 对象,而非原始值 |
| Symbol 键 | 大项目用 Symbol 避免命名冲突 |
| 默认值 | inject 选项可设 default,缺失时不报错 |
| Composition | provide(key, value) / inject(key, defaultValue) |
上一节:v-model 与表单组件绑定 下一节:Composition API:setup 与 ref/reactive
