import { getCurrentInstance, inject, provide } from 'vue' import type { InjectionKey } from 'vue' export interface CreateContextOptions { /** 严格模式:未找到 Provider 且没有 defaultValue 时是否抛错,默认 true */ strict?: boolean /** 默认值工厂:当没有 Provider 时调用,用返回值兜底,避免共享状态 */ defaultValue?: () => T /** 自定义 InjectionKey,一般不需要传 */ key?: InjectionKey } /** * 创建一套基于 provide/inject 的上下文工具。 * * 使用方式: * const FooContext = createContext('Foo', { * strict: true, * defaultValue: () => ({ ... }) * }) * * FooContext.provideContext(...) * const ctx = FooContext.useContext() */ export const createContext = (name?: string, options: CreateContextOptions = {}) => { const { strict = true, defaultValue, key } = options const contextKey: InjectionKey = key ?? (Symbol(name ?? 'Context') as InjectionKey) const useContext = () => { // 在 setup 之外调用没有意义,这里主动给出更友好的错误 if (!getCurrentInstance()) { throw new Error('useContext 只能在 setup 或生命周期钩子中使用') } const ctx = inject(contextKey, undefined) if (ctx !== undefined) { return ctx } if (defaultValue) { // 每次调用 defaultValue 都返回一个“新”默认值,避免跨实例共享 return defaultValue() } if (strict) { throw new Error( `未找到上层 Provider,请确保已经调用 provideContext 或在正确的组件树中使用(${name ?? 'Context'})` ) } return undefined as unknown as T } const provideContext = (context: T) => { provide(contextKey, context) } return { /** 当前上下文使用的 InjectionKey */ key: contextKey, /** 在上层组件中调用,用于提供上下文 */ provideContext, /** 在子组件中调用,用于消费上下文,如果未找到会抛出错误 / 或返回默认值 */ useContext } }