resolve-async-component.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  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. ;(owner: any).$on('hook:destroyed', () => remove(owners, owner))
  60. const forceRender = (renderCompleted: boolean) => {
  61. for (let i = 0, l = owners.length; i < l; i++) {
  62. (owners[i]: any).$forceUpdate()
  63. }
  64. if (renderCompleted) {
  65. owners.length = 0
  66. }
  67. }
  68. const resolve = once((res: Object | Class<Component>) => {
  69. // cache resolved
  70. factory.resolved = ensureCtor(res, baseCtor)
  71. // invoke callbacks only if this is not a synchronous resolve
  72. // (async resolves are shimmed as synchronous during SSR)
  73. if (!sync) {
  74. forceRender(true)
  75. } else {
  76. owners.length = 0
  77. }
  78. })
  79. const reject = once(reason => {
  80. process.env.NODE_ENV !== 'production' && warn(
  81. `Failed to resolve async component: ${String(factory)}` +
  82. (reason ? `\nReason: ${reason}` : '')
  83. )
  84. if (isDef(factory.errorComp)) {
  85. factory.error = true
  86. forceRender(true)
  87. }
  88. })
  89. const res = factory(resolve, reject)
  90. if (isObject(res)) {
  91. if (isPromise(res)) {
  92. // () => Promise
  93. if (isUndef(factory.resolved)) {
  94. res.then(resolve, reject)
  95. }
  96. } else if (isPromise(res.component)) {
  97. res.component.then(resolve, reject)
  98. if (isDef(res.error)) {
  99. factory.errorComp = ensureCtor(res.error, baseCtor)
  100. }
  101. if (isDef(res.loading)) {
  102. factory.loadingComp = ensureCtor(res.loading, baseCtor)
  103. if (res.delay === 0) {
  104. factory.loading = true
  105. } else {
  106. setTimeout(() => {
  107. if (isUndef(factory.resolved) && isUndef(factory.error)) {
  108. factory.loading = true
  109. forceRender(false)
  110. }
  111. }, res.delay || 200)
  112. }
  113. }
  114. if (isDef(res.timeout)) {
  115. setTimeout(() => {
  116. if (isUndef(factory.resolved)) {
  117. reject(
  118. process.env.NODE_ENV !== 'production'
  119. ? `timeout (${res.timeout}ms)`
  120. : null
  121. )
  122. }
  123. }, res.timeout)
  124. }
  125. }
  126. }
  127. sync = false
  128. // return in case resolved synchronously
  129. return factory.loading
  130. ? factory.loadingComp
  131. : factory.resolved
  132. }
  133. }