apiInject.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import { isFunction } from '@vue/shared'
  2. import { currentInstance } from './component'
  3. import { currentApp } from './apiCreateVaporApp'
  4. import { warn } from './warning'
  5. export interface InjectionKey<T> extends Symbol {}
  6. export function provide<T, K = InjectionKey<T> | string | number>(
  7. key: K,
  8. value: K extends InjectionKey<infer V> ? V : T,
  9. ) {
  10. if (!currentInstance) {
  11. if (__DEV__) {
  12. warn(`provide() can only be used inside setup().`)
  13. }
  14. } else {
  15. let provides = currentInstance.provides
  16. // by default an instance inherits its parent's provides object
  17. // but when it needs to provide values of its own, it creates its
  18. // own provides object using parent provides object as prototype.
  19. // this way in `inject` we can simply look up injections from direct
  20. // parent and let the prototype chain do the work.
  21. const parentProvides =
  22. currentInstance.parent && currentInstance.parent.provides
  23. if (parentProvides === provides) {
  24. provides = currentInstance.provides = Object.create(parentProvides)
  25. }
  26. // TS doesn't allow symbol as index type
  27. provides[key as string] = value
  28. }
  29. }
  30. export function inject<T>(key: InjectionKey<T> | string): T | undefined
  31. export function inject<T>(
  32. key: InjectionKey<T> | string,
  33. defaultValue: T,
  34. treatDefaultAsFactory?: false,
  35. ): T
  36. export function inject<T>(
  37. key: InjectionKey<T> | string,
  38. defaultValue: T | (() => T),
  39. treatDefaultAsFactory: true,
  40. ): T
  41. export function inject(
  42. key: InjectionKey<any> | string,
  43. defaultValue?: unknown,
  44. treatDefaultAsFactory = false,
  45. ) {
  46. const instance = currentInstance
  47. // also support looking up from app-level provides w/ `app.runWithContext()`
  48. if (instance || currentApp) {
  49. // #2400
  50. // to support `app.use` plugins,
  51. // fallback to appContext's `provides` if the instance is at root
  52. const provides = instance
  53. ? instance.parent == null
  54. ? instance.appContext && instance.appContext.provides
  55. : instance.parent.provides
  56. : currentApp!._context.provides
  57. if (provides && (key as string | symbol) in provides) {
  58. // TS doesn't allow symbol as index type
  59. return provides[key as string]
  60. } else if (arguments.length > 1) {
  61. return treatDefaultAsFactory && isFunction(defaultValue)
  62. ? defaultValue.call(instance && instance)
  63. : defaultValue
  64. } else if (__DEV__) {
  65. warn(`injection "${String(key)}" not found.`)
  66. }
  67. } else if (__DEV__) {
  68. warn(`inject() can only be used inside setup() or functional components.`)
  69. }
  70. }
  71. /**
  72. * Returns true if `inject()` can be used without warning about being called in the wrong place (e.g. outside of
  73. * setup()). This is used by libraries that want to use `inject()` internally without triggering a warning to the end
  74. * user. One example is `useRoute()` in `vue-router`.
  75. */
  76. export function hasInjectionContext(): boolean {
  77. return !!(currentInstance || currentApp)
  78. }