779daabf162637e5d197d59701c68657108be33b4f9480659443bc70b9879d62df08156ed6763b853d06bb0ef93f3e62cd55427a336f72d0148c860ec7c123 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /* @flow */
  2. import {
  3. warn,
  4. once,
  5. isDef,
  6. isUndef,
  7. isTrue,
  8. isObject,
  9. hasSymbol,
  10. isPromise,
  11. remove
  12. } from 'core/util/index'
  13. import { createEmptyVNode } from 'core/vdom/vnode'
  14. import { currentRenderingInstance } from 'core/instance/render'
  15. function ensureCtor (comp: any, base) {
  16. if (
  17. comp.__esModule ||
  18. (hasSymbol && comp[Symbol.toStringTag] === 'Module')
  19. ) {
  20. comp = comp.default
  21. }
  22. return isObject(comp)
  23. ? base.extend(comp)
  24. : comp
  25. }
  26. export function createAsyncPlaceholder (
  27. factory: Function,
  28. data: ?VNodeData,
  29. context: Component,
  30. children: ?Array<VNode>,
  31. tag: ?string
  32. ): VNode {
  33. const node = createEmptyVNode()
  34. node.asyncFactory = factory
  35. node.asyncMeta = { data, context, children, tag }
  36. return node
  37. }
  38. export function resolveAsyncComponent (
  39. factory: Function,
  40. baseCtor: Class<Component>
  41. ): Class<Component> | void {
  42. if (isTrue(factory.error) && isDef(factory.errorComp)) {
  43. return factory.errorComp
  44. }
  45. if (isDef(factory.resolved)) {
  46. return factory.resolved
  47. }
  48. const owner = currentRenderingInstance
  49. if (owner && isDef(factory.owners) && factory.owners.indexOf(owner) === -1) {
  50. // already pending
  51. factory.owners.push(owner)
  52. }
  53. if (isTrue(factory.loading) && isDef(factory.loadingComp)) {
  54. return factory.loadingComp
  55. }
  56. if (owner && !isDef(factory.owners)) {
  57. const owners = factory.owners = [owner]
  58. let sync = true
  59. let timerLoading = null
  60. let timerTimeout = null
  61. ;(owner: any).$on('hook:destroyed', () => remove(owners, owner))
  62. const forceRender = (renderCompleted: boolean) => {
  63. for (let i = 0, l = owners.length; i < l; i++) {
  64. (owners[i]: any).$forceUpdate()
  65. }
  66. if (renderCompleted) {
  67. owners.length = 0
  68. if (timerLoading !== null) {
  69. clearTimeout(timerLoading)
  70. timerLoading = null
  71. }
  72. if (timerTimeout !== null) {
  73. clearTimeout(timerTimeout)
  74. timerTimeout = null
  75. }
  76. }
  77. }
  78. const resolve = once((res: Object | Class<Component>) => {
  79. // cache resolved
  80. factory.resolved = ensureCtor(res, baseCtor)
  81. // invoke callbacks only if this is not a synchronous resolve
  82. // (async resolves are shimmed as synchronous during SSR)
  83. if (!sync) {
  84. forceRender(true)
  85. } else {
  86. owners.length = 0
  87. }
  88. })
  89. const reject = once(reason => {
  90. process.env.NODE_ENV !== 'production' && warn(
  91. `Failed to resolve async component: ${String(factory)}` +
  92. (reason ? `\nReason: ${reason}` : '')
  93. )
  94. if (isDef(factory.errorComp)) {
  95. factory.error = true
  96. forceRender(true)
  97. }
  98. })
  99. const res = factory(resolve, reject)
  100. if (isObject(res)) {
  101. if (isPromise(res)) {
  102. // () => Promise
  103. if (isUndef(factory.resolved)) {
  104. res.then(resolve, reject)
  105. }
  106. } else if (isPromise(res.component)) {
  107. res.component.then(resolve, reject)
  108. if (isDef(res.error)) {
  109. factory.errorComp = ensureCtor(res.error, baseCtor)
  110. }
  111. if (isDef(res.loading)) {
  112. factory.loadingComp = ensureCtor(res.loading, baseCtor)
  113. if (res.delay === 0) {
  114. factory.loading = true
  115. } else {
  116. timerLoading = setTimeout(() => {
  117. timerLoading = null
  118. if (isUndef(factory.resolved) && isUndef(factory.error)) {
  119. factory.loading = true
  120. forceRender(false)
  121. }
  122. }, res.delay || 200)
  123. }
  124. }
  125. if (isDef(res.timeout)) {
  126. timerTimeout = setTimeout(() => {
  127. timerTimeout = null
  128. if (isUndef(factory.resolved)) {
  129. reject(
  130. process.env.NODE_ENV !== 'production'
  131. ? `timeout (${res.timeout}ms)`
  132. : null
  133. )
  134. }
  135. }, res.timeout)
  136. }
  137. }
  138. }
  139. sync = false
  140. // return in case resolved synchronously
  141. return factory.loading
  142. ? factory.loadingComp
  143. : factory.resolved
  144. }
  145. }