Provide 和 Inject 命名冲突的根本原因是字符串 key 易重复,最稳妥解法是用 Symbol 作唯一标识符;通过单点定义、显式导入、类型约束(如 InjectionKey)实现不可重复、不可枚举、类型安全的依赖注入。
Provide 和 Inject 命名冲突的根本原因,是用字符串作为 key 时容易重复——尤其在多人协作或引入第三方组件库时。最稳妥的解法不是靠命名规范,而是用 Symbol 作唯一标识符,让 key 天然不可重复、不可枚举、不被意外覆盖。
每个 Symbol 都是唯一值,即使描述相同也互不相等。把 key 提前定义为常量,供 provide 和 inject 两端一致引用:
// keys.tsexport const ThemeKey = Symbol('theme')export const UserKey = Symbol('user')export const ApiClientKey = Symbol('api-client')
组件中使用时直接导入,不再拼写字符串:
// 父组件import { ThemeKey } from './keys.ts'provide(ThemeKey, reactive({ mode: 'dark' }))
// 子组件import { ThemeKey } from './keys.ts'const theme = inject(ThemeKey)
直接暴露 Symbol 常量虽安全,但耦合度高。更进一步,把 provide/inject 逻辑封装成 hook,隐藏 Symbol 细节:
// useTheme.tsconst ThemeKey = Symbol('theme')export function provideTheme(theme: Ref<Theme>) { provide(ThemeKey, theme)}export function useTheme() { const theme = inject(ThemeKey) if (!theme) throw new Error('useTheme must be used within a component that provides theme') return theme}
Symbol 本身支持泛型标注,配合 defineInject 的类型工具(如 VueUse 的 defineInject 或自定义类型),能实现 key 与 value 类型强绑定:
const CountKey = Symbol('count') as InjectionKey<Ref<number>>// provide 端provide(CountKey, countRef)// inject 端 —— 类型自动为 Ref<number>const count = inject(CountKey)
有人用命名空间字符串(如 'my-lib/theme')或 WeakMap 存储 Symbol,这些方式都有隐患:
Symbol(Math.random()))会导致两端 key 不一致,注入失败真正可靠的方案,始终是 单点定义 + 显式导入 + 类型约束。