vue@3.5.29
provide/inject 是 Vue3 中实现跨组件(任意层级)通信的核心 API。
provide:父组件 / 应用实例通过该 API 向下「提供」数据,可被所有后代组件访问;inject:后代组件通过该 API 「注入」祖先 / 应用提供的数据,无需逐层传递 props;- 解决的问题:
- 替代「props 透传」(多层组件传递 props 繁琐、代码冗余);
- 实现跨任意层级组件的通信(如祖孙组件、全局应用级数据共享)。
provide
provide 通过父组件 / 应用实例通过该 API 向下「提供」数据,可被所有后代组件访问。
/**
* provide 是 Vue3 组合式 API 中依赖注入的「提供方」核心函数,
* 用于在父组件 / 祖先组件中定义可被后代组件通过 inject 读取的响应式数据 / 方法,
* 实现跨组件层级的数据传递(无需逐层 props 透传)。
* @param key 注入键
* @param value 注入值
*/
export function provide<T, K = InjectionKey<T> | string | number>(
key: K,
value: K extends InjectionKey<infer V> ? V : T,
): void {
if (__DEV__) {
// / 无当前组件实例 或 组件已挂载 → 抛出警告
if (!currentInstance || currentInstance.isMounted) {
warn(`provide() can only be used inside setup().`)
}
}
if (currentInstance) {
// 获取当前组件的 provides 对象(默认继承父组件)
let provides = currentInstance.provides
// by default an instance inherits its parent's provides object
// but when it needs to provide values of its own, it creates its
// own provides object using parent provides object as prototype.
// this way in `inject` we can simply look up injections from direct
// parent and let the prototype chain do the work.
// 获取父组件的 provides 对象
const parentProvides =
currentInstance.parent && currentInstance.parent.provides
// 若当前 provides 与父 provides 指向同一对象(未初始化过)
if (parentProvides === provides) {
// 创建新的 provides 对象,原型链指向父 provides → 继承父的注入值
provides = currentInstance.provides = Object.create(parentProvides)
}
// TS doesn't allow symbol as index type
provides[key as string] = value
}
}
每个组件的 provides 对象通过 Object.create(parent.provides) 创建,因此原型链指向父组件 provides,实现「向上查找」。
let currentInstance: ComponentInternalInstance | null = null
/**
* 组件内部实例
* 每个 Vue 组件在运行时都会对应一个 ComponentInternalInstance 实例,Vue 内部通过这个实例掌控组件的一切行为。
* We expose a subset of properties on the internal instance as they are
* useful for advanced external libraries and tools.
*/
export interface ComponentInternalInstance {
uid: number // 组件唯一ID(全局递增,用于区分不同组件实例)
type: ConcreteComponent // 组件类型(如用户定义的 { setup(), template: ... })
parent: ComponentInternalInstance | null // 父组件实例(根组件为 null)
root: ComponentInternalInstance // 根组件实例(所有组件都指向根,方便快速访问)
appContext: AppContext // 组件所属的应用上下文(包含 app.config、全局组件/指令等)
/**
* 组件在父 VNode 树中的节点(父组件眼中的当前组件)
* Vnode representing this component in its parent's vdom tree
*/
vnode: VNode
/**
* 父组件更新时的「待生效新 VNode」(更新阶段临时存储)
* The pending new vnode from parent updates
* @internal
*/
next: VNode | null
/**
* 组件自身渲染出的「根 VNode 子树」(即组件内部的 DOM/子组件)
* Root vnode of this component's own vdom tree
*/
subTree: VNode
/**
* 组件的渲染副作用(响应式数据变化时触发重新渲染)
* Render effect instance
*/
effect: ReactiveEffect
/**
* 强制更新函数(对应 $forceUpdate,手动触发渲染)
* Force update render effect
*/
update: () => void
/**
* 渲染任务(交给调度器的函数,包含「脏检查」逻辑)
* Render effect job to be passed to scheduler (checks if dirty)
*/
job: SchedulerJob
/**
* 组件的渲染函数(编译 template 生成)
* The render function that returns vdom tree.
* @internal
*/
render: InternalRenderFunction | null
/**
* SSR 专用渲染函数
* SSR render function
* @internal
*/
ssrRender?: Function | null
/**
* Object containing values this component provides for its descendants
* @internal
*/
provides: Data
/**
* for tracking useId()
* first element is the current boundary prefix
* second number is the index of the useId call within that boundary
* @internal
*/
ids: [string, number, number]
/**
* Tracking reactive effects (e.g. watchers) associated with this component
* so that they can be automatically stopped on component unmount
* @internal
*/
scope: EffectScope
/**
* 代理访问类型缓存(避免频繁 hasOwnProperty 检查)
* cache for proxy access type to avoid hasOwnProperty calls
* @internal
*/
accessCache: Data | null
/**
* 渲染函数缓存(如内联事件处理器)
* cache for render function values that rely on _ctx but won't need updates
* after initialized (e.g. inline handlers)
* @internal
*/
renderCache: (Function | VNode | undefined)[]
/**
* 解析后的组件注册表(mixins/extends 场景)
* Resolved component registry, only for components with mixins or extends
* @internal
*/
components: Record<string, ConcreteComponent> | null
/**
* 解析后的指令注册表(mixins/extends 场景)
* Resolved directive registry, only for components with mixins or extends
* @internal
*/
directives: Record<string, Directive> | null
/**
* 过滤器(仅兼容 Vue2)
* Resolved filters registry, v2 compat only
* @internal
*/
filters?: Record<string, Function>
/**
* resolved props options
* @internal
*/
propsOptions: NormalizedPropsOptions
/**
* resolved emits options
* @internal
*/
emitsOptions: ObjectEmitsOptions | null
/**
* resolved inheritAttrs options
* @internal
*/
inheritAttrs?: boolean
/**
* 自定义元素实例
* Custom Element instance (if component is created by defineCustomElement)
* @internal
*/
ce?: ComponentCustomElementInterface
/**
* 是否是自定义元素
* is custom element? (kept only for compatibility)
* @internal
*/
isCE?: boolean
/**
* 自定义元素 HMR 方法
* custom element specific HMR method
* @internal
*/
ceReload?: (newStyles?: string[]) => void
// the rest are only for stateful components ---------------------------------
// main proxy that serves as the public instance (`this`)
// 组件公共实例(用户代码中的 `this`)
proxy: ComponentPublicInstance | null
// exposed properties via expose()
exposed: Record<string, any> | null // 通过 expose() 暴露的属性
exposeProxy: Record<string, any> | null // 暴露属性的代理(限制用户访问范围)
/**
* 运行时编译 render 函数的专用 proxy
* alternative proxy used only for runtime-compiled render functions using
* `with` block
* @internal
*/
withProxy: ComponentPublicInstance | null
/**
* 公共实例的目标对象(存储 computed、methods、自定义属性)
* This is the target for the public instance proxy. It also holds properties
* injected by user options (computed, methods etc.) and user-attached
* custom properties (via `this.x = ...`)
* @internal
*/
ctx: Data
// state
data: Data // 组件的 data 数据(响应式)
props: Data // 解析后的 props 数据(响应式)
attrs: Data // 组件的 attrs(非 props 的属性,如 class、style)
slots: InternalSlots // 组件的插槽(编译后的内部格式)
refs: Data // 组件的 refs 集合($refs)
emit: EmitFn // 组件的 emit 方法(触发自定义事件)
/**
* 跟踪 .once 事件是否已触发
* used for keeping track of .once event handlers on components
* @internal
*/
emitted: Record<string, boolean> | null
/**
* props 默认值缓存(避免默认工厂函数重复执行触发 watcher)
* used for caching the value returned from props default factory functions to
* avoid unnecessary watcher trigger
* @internal
*/
propsDefaults: Data
/**
* setup 函数返回的状态(响应式)
* setup related
* @internal
*/
setupState: Data
/**
* devtools access to additional info
* @internal
*/
devtoolsRawSetupState?: any
/**
* setup 函数的上下文(emit、slots、attrs 等)
* @internal
*/
setupContext: SetupContext | null
/**
* 所属的 Suspense 边界实例
* suspense related
* @internal
*/
suspense: SuspenseBoundary | null
/**
* Suspense 挂起批次 ID
* suspense pending batch id
* @internal
*/
suspenseId: number
/**
* 异步依赖(如异步组件的 Promise)
* @internal
*/
asyncDep: Promise<any> | null
/**
* 异步依赖是否已解析
* @internal
*/
asyncResolved: boolean
// lifecycle
isMounted: boolean // 是否已挂载
isUnmounted: boolean // 是否已卸载
isDeactivated: boolean // 是否被 KeepAlive 失活(隐藏)
/**
* @internal
*/
[LifecycleHooks.BEFORE_CREATE]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.CREATED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.BEFORE_MOUNT]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.MOUNTED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.BEFORE_UPDATE]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.UPDATED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.BEFORE_UNMOUNT]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.UNMOUNTED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.RENDER_TRACKED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.RENDER_TRIGGERED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.ACTIVATED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.DEACTIVATED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.ERROR_CAPTURED]: LifecycleHook
/**
* @internal
*/
[LifecycleHooks.SERVER_PREFETCH]: LifecycleHook<() => Promise<unknown>>
/**
* 缓存 $forceUpdate 方法(避免每次访问都绑定 this)
* For caching bound $forceUpdate on public proxy access
* @internal
*/
f?: () => void
/**
* 缓存 $nextTick 方法
* For caching bound $nextTick on public proxy access
* @internal
*/
n?: () => Promise<void>
/**
* 更新 Teleport 组件的 CSS 变量
* `updateTeleportCssVars`
* For updating css vars on contained teleports
* @internal
*/
ut?: (vars?: Record<string, unknown>) => void
/**
* 开发环境:样式 v-bind 水合校验
* dev only. For style v-bind hydration mismatch checks
* @internal
*/
getCssVars?: () => Record<string, unknown>
/**
* 合并后的 Vue2 风格选项
* v2 compat only, for caching mutated $options
* @internal
*/
resolvedOptions?: MergedComponentOptions
}
inject
inject 可以在后代组件通过该 API 注入祖先 / 应用提供的数据,无需逐层传递 props。
// 重载 1:仅传入 key(无默认值)/
export function inject<T>(key: InjectionKey<T> | string): T | undefined
// 重载 2:传入 key + 普通默认值
export function inject<T>(
key: InjectionKey<T> | string,
defaultValue: T,
treatDefaultAsFactory?: false,
): T
// 重载 3:传入 key + 工厂函数默认值
export function inject<T>(
key: InjectionKey<T> | string,
defaultValue: T | (() => T),
treatDefaultAsFactory: true,
): T
export function inject(
key: InjectionKey<any> | string,
defaultValue?: unknown,
treatDefaultAsFactory = false,·
) {
// fallback to `currentRenderingInstance` so that this can be called in
// a functional component
// 获取当前组件实例(兼容函数式组件,回退到 currentRenderingInstance)
const instance = getCurrentInstance()
// also support looking up from app-level provides w/ `app.runWithContext()`
// 仅当存在组件实例/应用上下文时执行核心逻辑
if (instance || currentApp) {
// #2400
// to support `app.use` plugins,
// fallback to appContext's `provides` if the instance is at root
// #11488, in a nested createApp, prioritize using the provides from currentApp
// #13212, for custom elements we must get injected values from its appContext
// as it already inherits the provides object from the parent element
// 确定要查找的 provides 对象
let provides = currentApp
? currentApp._context.provides // 场景1:有应用上下文 → 用应用级 provides
: instance
? instance.parent == null || instance.ce // 场景2:组件是根组件/自定义元素
// 从 vnode 的 appContext 取 provides
? instance.vnode.appContext && instance.vnode.appContext.provides
: instance.parent.provides // 普通组件 → 从父组件 provides 开始查找
: undefined
// 查找 key 并返回结果
if (provides && (key as string | symbol) in provides) {
// TS doesn't allow symbol as index type
return provides[key as string]
// 未找到 key,处理默认值(参数个数 > 1 表示传了 defaultValue)
} else if (arguments.length > 1) {
return treatDefaultAsFactory && isFunction(defaultValue)
? defaultValue.call(instance && instance.proxy)
: defaultValue
// 未找到 key 且无默认值 → 开发环境警告
} else if (__DEV__) {
warn(`injection "${String(key)}" not found.`)
}
// 无组件实例/应用上下文 → 开发环境警告(调用时机错误)
} else if (__DEV__) {
warn(`inject() can only be used inside setup() or functional components.`)
}
}
根组件 / 自定义元素会优先查找 appContext.provides(应用级注入)。