Skip to content

Commit d539788

Browse files
committed
feat: auto resolve ES module default when resolving async components
1 parent e55c89b commit d539788

File tree

3 files changed

+110
-98
lines changed

3 files changed

+110
-98
lines changed

src/history/base.js

+6-98
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22

33
import { _Vue } from '../install'
44
import type Router from '../index'
5-
import { warn } from '../util/warn'
65
import { inBrowser } from '../util/dom'
76
import { runQueue } from '../util/async'
7+
import { warn, isError } from '../util/warn'
88
import { START, isSameRoute } from '../util/route'
9+
import {
10+
flatten,
11+
flatMapComponents,
12+
resolveAsyncComponents
13+
} from '../util/resolve-components'
914

1015
export class History {
1116
router: Router;
@@ -321,100 +326,3 @@ function poll (
321326
}, 16)
322327
}
323328
}
324-
325-
function resolveAsyncComponents (matched: Array<RouteRecord>): Function {
326-
return (to, from, next) => {
327-
let hasAsync = false
328-
let pending = 0
329-
let error = null
330-
331-
flatMapComponents(matched, (def, _, match, key) => {
332-
// if it's a function and doesn't have cid attached,
333-
// assume it's an async component resolve function.
334-
// we are not using Vue's default async resolving mechanism because
335-
// we want to halt the navigation until the incoming component has been
336-
// resolved.
337-
if (typeof def === 'function' && def.cid === undefined) {
338-
hasAsync = true
339-
pending++
340-
341-
const resolve = once(resolvedDef => {
342-
// save resolved on async factory in case it's used elsewhere
343-
def.resolved = typeof resolvedDef === 'function'
344-
? resolvedDef
345-
: _Vue.extend(resolvedDef)
346-
match.components[key] = resolvedDef
347-
pending--
348-
if (pending <= 0) {
349-
next()
350-
}
351-
})
352-
353-
const reject = once(reason => {
354-
const msg = `Failed to resolve async component ${key}: ${reason}`
355-
process.env.NODE_ENV !== 'production' && warn(false, msg)
356-
if (!error) {
357-
error = isError(reason)
358-
? reason
359-
: new Error(msg)
360-
next(error)
361-
}
362-
})
363-
364-
let res
365-
try {
366-
res = def(resolve, reject)
367-
} catch (e) {
368-
reject(e)
369-
}
370-
if (res) {
371-
if (typeof res.then === 'function') {
372-
res.then(resolve, reject)
373-
} else {
374-
// new syntax in Vue 2.3
375-
const comp = res.component
376-
if (comp && typeof comp.then === 'function') {
377-
comp.then(resolve, reject)
378-
}
379-
}
380-
}
381-
}
382-
})
383-
384-
if (!hasAsync) next()
385-
}
386-
}
387-
388-
function flatMapComponents (
389-
matched: Array<RouteRecord>,
390-
fn: Function
391-
): Array<?Function> {
392-
return flatten(matched.map(m => {
393-
return Object.keys(m.components).map(key => fn(
394-
m.components[key],
395-
m.instances[key],
396-
m, key
397-
))
398-
}))
399-
}
400-
401-
function flatten (arr) {
402-
return Array.prototype.concat.apply([], arr)
403-
}
404-
405-
// in Webpack 2, require.ensure now also returns a Promise
406-
// so the resolve/reject functions may get called an extra time
407-
// if the user uses an arrow function shorthand that happens to
408-
// return that Promise.
409-
function once (fn) {
410-
let called = false
411-
return function (...args) {
412-
if (called) return
413-
called = true
414-
return fn.apply(this, args)
415-
}
416-
}
417-
418-
function isError (err) {
419-
return Object.prototype.toString.call(err).indexOf('Error') > -1
420-
}

src/util/resolve-components.js

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

src/util/warn.js

+4
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ export function warn (condition: any, message: string) {
1111
typeof console !== 'undefined' && console.warn(`[vue-router] ${message}`)
1212
}
1313
}
14+
15+
export function isError (err: any): boolean {
16+
return Object.prototype.toString.call(err).indexOf('Error') > -1
17+
}

0 commit comments

Comments
 (0)