component.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import { isArray, isFunction, isObject, isPromise } from '@vue/shared'
  2. import { defineAsyncComponent } from '../apiAsyncComponent'
  3. import {
  4. Component,
  5. ComponentInternalInstance,
  6. ComponentOptions,
  7. FunctionalComponent,
  8. getCurrentInstance
  9. } from '../component'
  10. import { resolveInjections } from '../componentOptions'
  11. import { InternalSlots } from '../componentSlots'
  12. import { isVNode } from '../vnode'
  13. import {
  14. checkCompatEnabled,
  15. softAssertCompatEnabled,
  16. DeprecationTypes
  17. } from './compatConfig'
  18. import { getCompatListeners } from './instanceListeners'
  19. import { compatH } from './renderFn'
  20. export function convertLegacyComponent(
  21. comp: any,
  22. instance: ComponentInternalInstance | null
  23. ): Component {
  24. if (comp.__isBuiltIn) {
  25. return comp
  26. }
  27. // 2.x constructor
  28. if (isFunction(comp) && comp.cid) {
  29. comp = comp.options
  30. }
  31. // 2.x async component
  32. if (
  33. isFunction(comp) &&
  34. checkCompatEnabled(DeprecationTypes.COMPONENT_ASYNC, instance, comp)
  35. ) {
  36. // since after disabling this, plain functions are still valid usage, do not
  37. // use softAssert here.
  38. return convertLegacyAsyncComponent(comp)
  39. }
  40. // 2.x functional component
  41. if (
  42. isObject(comp) &&
  43. comp.functional &&
  44. softAssertCompatEnabled(
  45. DeprecationTypes.COMPONENT_FUNCTIONAL,
  46. instance,
  47. comp
  48. )
  49. ) {
  50. return convertLegacyFunctionalComponent(comp)
  51. }
  52. return comp
  53. }
  54. interface LegacyAsyncOptions {
  55. component: Promise<Component>
  56. loading?: Component
  57. error?: Component
  58. delay?: number
  59. timeout?: number
  60. }
  61. type LegacyAsyncReturnValue = Promise<Component> | LegacyAsyncOptions
  62. type LegacyAsyncComponent = (
  63. resolve?: (res: LegacyAsyncReturnValue) => void,
  64. reject?: (reason?: any) => void
  65. ) => LegacyAsyncReturnValue | undefined
  66. const normalizedAsyncComponentMap = new Map<LegacyAsyncComponent, Component>()
  67. function convertLegacyAsyncComponent(comp: LegacyAsyncComponent) {
  68. if (normalizedAsyncComponentMap.has(comp)) {
  69. return normalizedAsyncComponentMap.get(comp)!
  70. }
  71. // we have to call the function here due to how v2's API won't expose the
  72. // options until we call it
  73. let resolve: (res: LegacyAsyncReturnValue) => void
  74. let reject: (reason?: any) => void
  75. const fallbackPromise = new Promise<Component>((r, rj) => {
  76. ;(resolve = r), (reject = rj)
  77. })
  78. const res = comp(resolve!, reject!)
  79. let converted: Component
  80. if (isPromise(res)) {
  81. converted = defineAsyncComponent(() => res)
  82. } else if (isObject(res) && !isVNode(res) && !isArray(res)) {
  83. converted = defineAsyncComponent({
  84. loader: () => res.component,
  85. loadingComponent: res.loading,
  86. errorComponent: res.error,
  87. delay: res.delay,
  88. timeout: res.timeout
  89. })
  90. } else if (res == null) {
  91. converted = defineAsyncComponent(() => fallbackPromise)
  92. } else {
  93. converted = comp as any // probably a v3 functional comp
  94. }
  95. normalizedAsyncComponentMap.set(comp, converted)
  96. return converted
  97. }
  98. const normalizedFunctionalComponentMap = new Map<
  99. ComponentOptions,
  100. FunctionalComponent
  101. >()
  102. export const legacySlotProxyHandlers: ProxyHandler<InternalSlots> = {
  103. get(target, key: string) {
  104. const slot = target[key]
  105. return slot && slot()
  106. }
  107. }
  108. function convertLegacyFunctionalComponent(comp: ComponentOptions) {
  109. if (normalizedFunctionalComponentMap.has(comp)) {
  110. return normalizedFunctionalComponentMap.get(comp)!
  111. }
  112. const legacyFn = comp.render as any
  113. const Func: FunctionalComponent = (props, ctx) => {
  114. const instance = getCurrentInstance()!
  115. const legacyCtx = {
  116. props,
  117. children: instance.vnode.children || [],
  118. data: instance.vnode.props || {},
  119. scopedSlots: ctx.slots,
  120. parent: instance.parent && instance.parent.proxy,
  121. slots() {
  122. return new Proxy(ctx.slots, legacySlotProxyHandlers)
  123. },
  124. get listeners() {
  125. return getCompatListeners(instance)
  126. },
  127. get injections() {
  128. if (comp.inject) {
  129. const injections = {}
  130. resolveInjections(comp.inject, {})
  131. return injections
  132. }
  133. return {}
  134. }
  135. }
  136. return legacyFn(compatH, legacyCtx)
  137. }
  138. Func.props = comp.props
  139. Func.displayName = comp.name
  140. // v2 functional components do not inherit attrs
  141. Func.inheritAttrs = false
  142. normalizedFunctionalComponentMap.set(comp, Func)
  143. return Func
  144. }