标准答案
- provide 由祖先组件提供值,inject 由后代组件读取值,可以避免多层级 props drilling。
- 它适合传局部上下文,例如 Form 提供校验方法,Tabs 提供当前激活项,ConfigProvider 提供局部配置,ThemeProvider 提供主题变量。
- 如果传入 ref、reactive 或 computed,后代拿到的仍然可以保持响应式。普通值不会自动变成响应式。
- provide / inject 不适合替代父子 props。只有一两层父子关系时,props 和 emits 更清楚。
- 它也不适合承载复杂全局业务状态。跨页面、跨模块共享状态应优先考虑 Pinia / Vuex 或清晰的数据层。
题目解析
provide / inject 更像“组件子树上下文”,不是全局变量。
使用它时要注意来源不如 props 明显。大型项目最好给 key 命名清楚,必要时用 Symbol 防止冲突。
注入的可变对象要控制修改入口。否则后代组件都能改同一份状态,边界会变模糊。
代码示例
表单组件可以 provide 校验上下文,子级表单项通过 inject 读取。
Vue
<!-- FormRoot.vue -->
<script setup lang="ts">
import { provide, reactive } from 'vue'
const formState = reactive({
errors: new Map<string, string>()
})
function validateField(name: string) {
console.log('validate', name)
}
provide('formContext', {
formState,
validateField
})
</script>
<template>
<form>
<slot />
</form>
</template>Vue
<!-- FormItem.vue -->
<script setup lang="ts">
import { inject } from 'vue'
const form = inject<{
validateField: (name: string) => void
}>('formContext')
function blurName() {
form?.validateField('name')
}
</script>常见误区
- 一层父子组件也用 provide / inject,导致数据来源不如 props 清楚。
- 把全站用户信息、权限、购物车等复杂状态都塞进 provide,绕开状态管理。
- provide 普通值后期待它自动响应式更新。需要响应式时要提供 ref、reactive 或 computed。