Skip to content

Commit 3455d74

Browse files
committed
fix async component resolving
1 parent 45f72f9 commit 3455d74

File tree

7 files changed

+110
-61
lines changed

7 files changed

+110
-61
lines changed

src/create-matcher.js

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,44 +33,18 @@ export function createMatcher (routes: Array<RouteConfig>): Matcher {
3333
const record = nameMap[name]
3434
if (record) {
3535
location.path = fillParams(record.path, location.params, `named route "${name}"`)
36-
return createRouteContext(record, location, redirectedFrom)
36+
return _createRoute(record, location, redirectedFrom)
3737
}
3838
} else if (location.path) {
3939
location.params = {}
4040
for (const path in pathMap) {
4141
if (matchRoute(path, location.params, location.path)) {
42-
return createRouteContext(pathMap[path], location, redirectedFrom)
42+
return _createRoute(pathMap[path], location, redirectedFrom)
4343
}
4444
}
4545
}
4646
// no match
47-
return createRouteContext(null, location)
48-
}
49-
50-
function createRouteContext (
51-
record: ?RouteRecord,
52-
location: Location,
53-
redirectedFrom?: Location
54-
): Route {
55-
if (record && record.redirect) {
56-
return redirect(record, redirectedFrom || location)
57-
}
58-
if (record && record.matchAs) {
59-
return alias(record, location, record.matchAs)
60-
}
61-
const route: Route = {
62-
name: location.name,
63-
path: location.path || '/',
64-
hash: location.hash || '',
65-
query: location.query || {},
66-
params: location.params || {},
67-
fullPath: getFullPath(location),
68-
matched: record ? formatMatch(record) : []
69-
}
70-
if (redirectedFrom) {
71-
route.redirectedFrom = getFullPath(redirectedFrom)
72-
}
73-
return Object.freeze(route)
47+
return _createRoute(null, location)
7448
}
7549

7650
function redirect (
@@ -105,7 +79,7 @@ export function createMatcher (routes: Array<RouteConfig>): Matcher {
10579
}, undefined, location)
10680
} else {
10781
warn(false, `invalid redirect option: ${JSON.stringify(redirect)}`)
108-
return createRouteContext(null, location)
82+
return _createRoute(null, location)
10983
}
11084
}
11185

@@ -123,14 +97,48 @@ export function createMatcher (routes: Array<RouteConfig>): Matcher {
12397
const matched = aliasedMatch.matched
12498
const aliasedRecord = matched[matched.length - 1]
12599
location.params = aliasedMatch.params
126-
return createRouteContext(aliasedRecord, location)
100+
return _createRoute(aliasedRecord, location)
101+
}
102+
return _createRoute(null, location)
103+
}
104+
105+
function _createRoute (
106+
record: ?RouteRecord,
107+
location: Location,
108+
redirectedFrom?: Location
109+
): Route {
110+
if (record && record.redirect) {
111+
return redirect(record, redirectedFrom || location)
127112
}
128-
return createRouteContext(null, location)
113+
if (record && record.matchAs) {
114+
return alias(record, location, record.matchAs)
115+
}
116+
return createRoute(record, location, redirectedFrom)
129117
}
130118

131119
return match
132120
}
133121

122+
export function createRoute (
123+
record: ?RouteRecord,
124+
location: Location,
125+
redirectedFrom?: Location
126+
): Route {
127+
const route: Route = {
128+
name: location.name,
129+
path: location.path || '/',
130+
hash: location.hash || '',
131+
query: location.query || {},
132+
params: location.params || {},
133+
fullPath: getFullPath(location),
134+
matched: record ? formatMatch(record) : []
135+
}
136+
if (redirectedFrom) {
137+
route.redirectedFrom = getFullPath(redirectedFrom)
138+
}
139+
return Object.freeze(route)
140+
}
141+
134142
function matchRoute (
135143
path: string,
136144
params: Object,

src/history/abstract.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ export class AbstractHistory extends History {
99

1010
constructor (router: VueRouter) {
1111
super(router)
12-
this.stack = [this.current]
12+
this.stack = []
1313
this.index = 0
1414
}
1515

16+
onInit () {
17+
this.stack = [this.current]
18+
}
19+
1620
push (location: RawLocation) {
1721
super.transitionTo(location, route => {
1822
this.stack = this.stack.slice(0, this.index + 1).concat(route)

src/history/base.js

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type VueRouter from '../index'
44
import { inBrowser } from '../util/dom'
55
import { runQueue } from '../util/async'
66
import { isSameRoute } from '../util/route'
7+
import { createRoute } from '../create-matcher'
78

89
export class History {
910
router: VueRouter;
@@ -16,13 +17,19 @@ export class History {
1617
go: Function;
1718
push: Function;
1819
replace: Function;
20+
onInit: Function;
1921

2022
constructor (router: VueRouter, base: ?string) {
2123
this.router = router
2224
this.base = normalizeBae(base)
23-
this.current = router.match('/')
25+
// start with a route object that stands for "nowhere"
26+
this.current = createRoute(null, {
27+
path: '__vue_router_init__'
28+
})
2429
this.pending = null
25-
this.transitionTo(this.getLocation())
30+
this.transitionTo(this.getLocation(), route => {
31+
this.onInit(route)
32+
})
2633
}
2734

2835
listen (cb: Function) {
@@ -53,7 +60,9 @@ export class History {
5360
// global before hooks
5461
this.router.beforeHooks,
5562
// activate guards
56-
activated.map(m => m.beforeEnter)
63+
activated.map(m => m.beforeEnter),
64+
// async components
65+
resolveAsyncComponents(activated)
5766
).filter(_ => _)
5867

5968
this.pending = route
@@ -123,18 +132,41 @@ function resolveQueue (
123132
}
124133

125134
function extractLeaveGuards (matched: Array<RouteRecord>): Array<?Function> {
126-
return Array.prototype.concat.apply([], matched.map(m => {
127-
return Object.keys(m.components).map(key => {
128-
const component = m.components[key]
129-
const instance = m.instances[key] && m.instances[key].child
130-
const guard = typeof component === 'function'
131-
? component.options.beforeRouteLeave
132-
: (component && component.beforeRouteLeave)
133-
if (guard) {
134-
return function routeGuard () {
135-
return guard.apply(instance, arguments)
136-
}
135+
return flatMapComponents(matched, (def, instance) => {
136+
const guard = def && def.beforeRouteLeave
137+
if (guard) {
138+
return function routeGuard () {
139+
return guard.apply(instance, arguments)
137140
}
138-
})
139-
}).reverse())
141+
}
142+
}).reverse()
143+
}
144+
145+
function resolveAsyncComponents (matched: Array<RouteRecord>): Array<?Function> {
146+
return flatMapComponents(matched, (def, _, match, key) => {
147+
// if it's a function and doesn't have Vue options attached,
148+
// assume it's an async component resolve function.
149+
// we are not using Vue's default async resolving mechanism because
150+
// we want to halt the navigation until the incoming component has been
151+
// resolved.
152+
if (typeof def === 'function' && !def.options) {
153+
return (route, redirect, next) => def(resolvedDef => {
154+
match.components[key] = resolvedDef
155+
next()
156+
})
157+
}
158+
})
159+
}
160+
161+
function flatMapComponents (
162+
matched: Array<RouteRecord>,
163+
fn: Function
164+
): Array<?Function> {
165+
return Array.prototype.concat.apply([], matched.map(m => {
166+
return Object.keys(m.components).map(key => fn(
167+
m.components[key],
168+
m.instances[key] && m.instances[key].child,
169+
m, key
170+
))
171+
}))
140172
}

src/history/hash.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,17 @@ export class HashHistory extends History {
1212
if (fallback && this.checkFallback()) {
1313
return
1414
}
15+
window.addEventListener('hashchange', () => {
16+
this.onHashChange()
17+
})
18+
}
19+
20+
onInit () {
1521
ensureSlash()
1622
// possible redirect on start
1723
if (getHash() !== this.current.fullPath) {
1824
replaceHash(this.current.fullPath)
1925
}
20-
window.addEventListener('hashchange', () => {
21-
this.onHashChange()
22-
})
2326
}
2427

2528
checkFallback () {

src/history/html5.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,7 @@ export class HTML5History extends History {
1919
constructor (router: VueRouter, base: ?string) {
2020
super(router, base)
2121

22-
// possible redirect on start
23-
const url = cleanPath(this.base + this.current.fullPath)
24-
if (this.getLocation() !== url) {
25-
replaceState(url)
26-
}
27-
2822
const expectScroll = router.options.scrollBehavior
29-
3023
window.addEventListener('popstate', e => {
3124
_key = e.state && e.state.key
3225
const current = this.current
@@ -44,6 +37,14 @@ export class HTML5History extends History {
4437
}
4538
}
4639

40+
onInit () {
41+
// possible redirect on start
42+
const url = cleanPath(this.base + this.current.fullPath)
43+
if (this.getLocation() !== url) {
44+
replaceState(url)
45+
}
46+
}
47+
4748
go (n: number) {
4849
window.history.go(n)
4950
}

src/util/location.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ export function normalizeLocation (
1414
}
1515

1616
const parsedPath = parsePath(next.path || '')
17+
const basePath = (current && current.path) || '/'
1718
const path = parsedPath.path
18-
? resolvePath(parsedPath.path, current && current.path, append)
19+
? resolvePath(parsedPath.path, basePath, append)
1920
: (current && current.path) || '/'
2021
const query = resolveQuery(parsedPath.query, next.query)
2122
let hash = next.hash || parsedPath.hash

src/util/path.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
export function resolvePath (
44
relative: string,
5-
base: string = '/',
5+
base: string,
66
append?: boolean
77
): string {
88
if (relative.charAt(0) === '/') {

0 commit comments

Comments
 (0)