标准答案

  1. provide 由祖先组件提供值,inject 由后代组件读取值,可以避免多层级 props drilling。
  2. 它适合传局部上下文,例如 Form 提供校验方法,Tabs 提供当前激活项,ConfigProvider 提供局部配置,ThemeProvider 提供主题变量。
  3. 如果传入 ref、reactive 或 computed,后代拿到的仍然可以保持响应式。普通值不会自动变成响应式。
  4. provide / inject 不适合替代父子 props。只有一两层父子关系时,props 和 emits 更清楚。
  5. 它也不适合承载复杂全局业务状态。跨页面、跨模块共享状态应优先考虑 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。

作者信息