componentCurrentInstance.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import { getGlobalThis } from '@vue/shared'
  2. import type {
  3. ComponentInternalInstance,
  4. GenericComponentInstance,
  5. } from './component'
  6. import { currentRenderingInstance } from './componentRenderContext'
  7. import { type EffectScope, setCurrentScope } from '@vue/reactivity'
  8. import { warn } from './warning'
  9. /**
  10. * @internal
  11. */
  12. export let currentInstance: GenericComponentInstance | null = null
  13. /**
  14. * @internal
  15. */
  16. export const getCurrentGenericInstance: () => GenericComponentInstance | null =
  17. () => currentInstance || currentRenderingInstance
  18. export const getCurrentInstance: () => ComponentInternalInstance | null = () =>
  19. currentInstance && !currentInstance.vapor
  20. ? (currentInstance as ComponentInternalInstance)
  21. : currentRenderingInstance
  22. export let isInSSRComponentSetup = false
  23. export let setInSSRSetupState: (state: boolean) => void
  24. /**
  25. * @internal
  26. */
  27. export let simpleSetCurrentInstance: (
  28. instance: GenericComponentInstance | null,
  29. ) => void
  30. /**
  31. * The following makes getCurrentInstance() usage across multiple copies of Vue
  32. * work. Some cases of how this can happen are summarized in #7590. In principle
  33. * the duplication should be avoided, but in practice there are often cases
  34. * where the user is unable to resolve on their own, especially in complicated
  35. * SSR setups.
  36. *
  37. * Note this fix is technically incomplete, as we still rely on other singletons
  38. * for effectScope and global reactive dependency maps. However, it does make
  39. * some of the most common cases work. It also warns if the duplication is
  40. * found during browser execution.
  41. */
  42. if (__SSR__) {
  43. type Setter = (v: any) => void
  44. const g = getGlobalThis()
  45. const registerGlobalSetter = (key: string, setter: Setter) => {
  46. let setters: Setter[]
  47. if (!(setters = g[key])) setters = g[key] = []
  48. setters.push(setter)
  49. return (v: any) => {
  50. if (setters.length > 1) setters.forEach(set => set(v))
  51. else setters[0](v)
  52. }
  53. }
  54. simpleSetCurrentInstance = registerGlobalSetter(
  55. `__VUE_INSTANCE_SETTERS__`,
  56. v => (currentInstance = v),
  57. )
  58. // also make `isInSSRComponentSetup` sharable across copies of Vue.
  59. // this is needed in the SFC playground when SSRing async components, since
  60. // we have to load both the runtime and the server-renderer from CDNs, they
  61. // contain duplicated copies of Vue runtime code.
  62. setInSSRSetupState = registerGlobalSetter(
  63. `__VUE_SSR_SETTERS__`,
  64. v => (isInSSRComponentSetup = v),
  65. )
  66. } else {
  67. simpleSetCurrentInstance = i => {
  68. currentInstance = i
  69. }
  70. setInSSRSetupState = v => {
  71. isInSSRComponentSetup = v
  72. }
  73. }
  74. export const setCurrentInstance = (
  75. instance: GenericComponentInstance | null,
  76. scope: EffectScope | undefined = instance !== null
  77. ? instance.scope
  78. : undefined,
  79. ): [GenericComponentInstance | null, EffectScope | undefined] => {
  80. try {
  81. return [currentInstance, setCurrentScope(scope)]
  82. } finally {
  83. simpleSetCurrentInstance(instance)
  84. }
  85. }
  86. const internalOptions = ['ce', 'type'] as const
  87. /**
  88. * @internal
  89. */
  90. export const useInstanceOption = <K extends (typeof internalOptions)[number]>(
  91. key: K,
  92. silent = false,
  93. ): {
  94. hasInstance: boolean
  95. value: GenericComponentInstance[K] | undefined
  96. } => {
  97. const instance = getCurrentGenericInstance()
  98. if (!instance) {
  99. if (__DEV__ && !silent) {
  100. warn(`useInstanceOption called without an active component instance.`)
  101. }
  102. return { hasInstance: false, value: undefined }
  103. }
  104. if (!internalOptions.includes(key)) {
  105. if (__DEV__) {
  106. warn(
  107. `useInstanceOption only accepts ` +
  108. ` ${internalOptions.map(k => `'${k}'`).join(', ')} as key, got '${key}'.`,
  109. )
  110. }
  111. return { hasInstance: true, value: undefined }
  112. }
  113. return { hasInstance: true, value: instance[key] }
  114. }