5aa7e1e3b7148a74e221de31b503b4e1dd88a12e367c1a74328bcfd8ee6e1dc6ca211cbfa90561049343616a274ed618114e205a4d11a2dd0ee9f54aa3c1a0 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. /* @flow */
  2. import { _Vue } from '../install'
  3. import { warn } from './warn'
  4. import { isError } from '../util/errors'
  5. export function resolveAsyncComponents (matched: Array<RouteRecord>): Function {
  6. return (to, from, next) => {
  7. let hasAsync = false
  8. let pending = 0
  9. let error = null
  10. flatMapComponents(matched, (def, _, match, key) => {
  11. // if it's a function and doesn't have cid attached,
  12. // assume it's an async component resolve function.
  13. // we are not using Vue's default async resolving mechanism because
  14. // we want to halt the navigation until the incoming component has been
  15. // resolved.
  16. if (typeof def === 'function' && def.cid === undefined) {
  17. hasAsync = true
  18. pending++
  19. const resolve = once(resolvedDef => {
  20. if (isESModule(resolvedDef)) {
  21. resolvedDef = resolvedDef.default
  22. }
  23. // save resolved on async factory in case it's used elsewhere
  24. def.resolved = typeof resolvedDef === 'function'
  25. ? resolvedDef
  26. : _Vue.extend(resolvedDef)
  27. match.components[key] = resolvedDef
  28. pending--
  29. if (pending <= 0) {
  30. next()
  31. }
  32. })
  33. const reject = once(reason => {
  34. const msg = `Failed to resolve async component ${key}: ${reason}`
  35. process.env.NODE_ENV !== 'production' && warn(false, msg)
  36. if (!error) {
  37. error = isError(reason)
  38. ? reason
  39. : new Error(msg)
  40. next(error)
  41. }
  42. })
  43. let res
  44. try {
  45. res = def(resolve, reject)
  46. } catch (e) {
  47. reject(e)
  48. }
  49. if (res) {
  50. if (typeof res.then === 'function') {
  51. res.then(resolve, reject)
  52. } else {
  53. // new syntax in Vue 2.3
  54. const comp = res.component
  55. if (comp && typeof comp.then === 'function') {
  56. comp.then(resolve, reject)
  57. }
  58. }
  59. }
  60. }
  61. })
  62. if (!hasAsync) next()
  63. }
  64. }
  65. export function flatMapComponents (
  66. matched: Array<RouteRecord>,
  67. fn: Function
  68. ): Array<?Function> {
  69. return flatten(matched.map(m => {
  70. return Object.keys(m.components).map(key => fn(
  71. m.components[key],
  72. m.instances[key],
  73. m, key
  74. ))
  75. }))
  76. }
  77. export function flatten (arr: Array<any>): Array<any> {
  78. return Array.prototype.concat.apply([], arr)
  79. }
  80. const hasSymbol =
  81. typeof Symbol === 'function' &&
  82. typeof Symbol.toStringTag === 'symbol'
  83. function isESModule (obj) {
  84. return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module')
  85. }
  86. // in Webpack 2, require.ensure now also returns a Promise
  87. // so the resolve/reject functions may get called an extra time
  88. // if the user uses an arrow function shorthand that happens to
  89. // return that Promise.
  90. function once (fn) {
  91. let called = false
  92. return function (...args) {
  93. if (called) return
  94. called = true
  95. return fn.apply(this, args)
  96. }
  97. }