一、核心概念
-
作用:允许祖先组件 (provider)向任意层级后代(injector)传递数据,而无需经过每一层 props 逐级下发或事件逐级上报。
-
语义:
-
provide:在祖先组件里"声明"要下发的数据。
-
inject:在后代组件里"声明"要接收的数据。
-
-
响应式差异:
-
Vue2 默认不具备响应式联动(provide 的值在运行时变了,后代不会自动更新)。
-
Vue3 只要 provide 的是
ref / reactive对象,后代就能自动追踪变化。
-
二、Vue2 示例(Options API)
javascript
<template>
<div>
<h3>根组件 (Vue2) 主题: {{theme}}</h3>
<button @click="toggleTheme">切换主题</button>
<hr>
<Child/>
</div>
</template>
<script>
import Child from './Child.vue'
export default {
components: { Child },
data() {
return {
theme: 'light' // 普通字符串,非响应式
}
},
provide() { // 关键点1:函数写法,才能访问 this
return {
theme: this.theme, // 注意:仅初始化时快照,后续变化不会同步
toggleTheme: this.toggleTheme
}
},
methods: {
toggleTheme() {
this.theme = this.theme === 'light' ? 'dark' : 'light'
}
}
}
</script>
子孙组件
javascript
<template>
<div style="margin-left: 40px;">
<p>孙子组件拿到主题:{{theme}}</p>
<button @click="toggleTheme">孙子触发切换</button>
</div>
</template>
<script>
export default {
inject: ['theme', 'toggleTheme'], // 关键点2:数组语法
created() {
console.log('Vue2 inject 到的数据:', this.theme)
}
}
</script>
运行结果:
-
初始能读到 theme;
-
根组件按钮可切换,但孙子不会自动刷新(非响应式)。
三、Vue3 示例(Composition API)
javascript
<template>
<div>
<h3>根组件 (Vue3) 主题: {{theme}}</h3>
<button @click="toggleTheme">切换主题</button>
<hr>
<Child/>
</div>
</template>
<script setup>
import { provide, ref, readonly } from 'vue'
import Child from './Child.vue'
const theme = ref('light')
function toggleTheme() {
theme.value = theme.value === 'light' ? 'dark' : 'light'
}
// 关键点1:使用 provide(key, value)
provide('theme', readonly(theme)) // 只读保护,防止被随意修改
provide('toggleTheme', toggleTheme)
</script>
子孙组件
javascript
<template>
<div style="margin-left: 40px;">
<p>孙子组件拿到主题:{{theme}}</p>
<button @click="toggleTheme">孙子触发切换</button>
</div>
</template>
<script setup>
import { inject } from 'vue'
const theme = inject('theme') // 关键点2: inject(key, 默认值?)
const toggleTheme = inject('toggleTheme')
// 默认值示例
const user = inject('user', { name: 'Guest' })
</script>
运行结果:
- 根组件切换按钮,孙子立即同步刷新(响应式)。
四、常见陷阱速查
-
Vue2 中 provide 写成对象而非函数,导致访问不到实例属性。
-
把非响应式字面量(如普通字符串、数字)provide 出去,后期改动后代看不到。
-
直接修改 inject 拿到的响应式对象,会绕过单向数据流;最好
provide('xxx', readonly(xxx))。 -
跨组件层级很深时,provide/inject 比 EventBus 或 Vuex/Pinia 更轻量;但全局状态仍推荐官方状态管理库。