kdu-router 3.1.7 → 3.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -11
- package/dist/kdu-router.common.js +640 -410
- package/dist/kdu-router.esm.browser.js +617 -371
- package/dist/kdu-router.esm.browser.min.js +3 -3
- package/dist/kdu-router.esm.js +640 -410
- package/dist/kdu-router.js +644 -415
- package/dist/kdu-router.min.js +3 -3
- package/ketur/attributes.json +38 -0
- package/ketur/tags.json +20 -0
- package/package.json +23 -14
- package/src/components/link.js +38 -4
- package/src/components/view.js +7 -1
- package/src/create-matcher.js +28 -2
- package/src/create-route-map.js +18 -3
- package/src/history/abstract.js +6 -3
- package/src/history/base.js +91 -64
- package/src/history/hash.js +25 -27
- package/src/history/html5.js +25 -6
- package/src/index.js +76 -44
- package/src/util/errors.js +86 -0
- package/src/util/path.js +1 -1
- package/src/util/push-state.js +1 -1
- package/src/util/query.js +49 -31
- package/src/util/resolve-components.js +2 -1
- package/src/util/route.js +29 -10
- package/src/util/scroll.js +26 -7
- package/src/util/warn.js +1 -12
- package/types/index.d.ts +6 -1
- package/types/router.d.ts +77 -11
- package/src/history/errors.js +0 -22
package/src/history/base.js
CHANGED
|
@@ -4,14 +4,23 @@ import { _Kdu } from '../install'
|
|
|
4
4
|
import type Router from '../index'
|
|
5
5
|
import { inBrowser } from '../util/dom'
|
|
6
6
|
import { runQueue } from '../util/async'
|
|
7
|
-
import { warn
|
|
8
|
-
import { START, isSameRoute } from '../util/route'
|
|
7
|
+
import { warn } from '../util/warn'
|
|
8
|
+
import { START, isSameRoute, handleRouteEntered } from '../util/route'
|
|
9
9
|
import {
|
|
10
10
|
flatten,
|
|
11
11
|
flatMapComponents,
|
|
12
12
|
resolveAsyncComponents
|
|
13
13
|
} from '../util/resolve-components'
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
createNavigationDuplicatedError,
|
|
16
|
+
createNavigationCancelledError,
|
|
17
|
+
createNavigationRedirectedError,
|
|
18
|
+
createNavigationAbortedError,
|
|
19
|
+
isError,
|
|
20
|
+
isNavigationFailure,
|
|
21
|
+
NavigationFailureType
|
|
22
|
+
} from '../util/errors'
|
|
23
|
+
import { handleScroll } from '../util/scroll'
|
|
15
24
|
|
|
16
25
|
export class History {
|
|
17
26
|
router: Router
|
|
@@ -23,13 +32,20 @@ export class History {
|
|
|
23
32
|
readyCbs: Array<Function>
|
|
24
33
|
readyErrorCbs: Array<Function>
|
|
25
34
|
errorCbs: Array<Function>
|
|
35
|
+
listeners: Array<Function>
|
|
36
|
+
cleanupListeners: Function
|
|
26
37
|
|
|
27
38
|
// implemented by sub-classes
|
|
28
39
|
+go: (n: number) => void
|
|
29
|
-
+push: (loc: RawLocation) => void
|
|
30
|
-
+replace: (
|
|
40
|
+
+push: (loc: RawLocation, onComplete?: Function, onAbort?: Function) => void
|
|
41
|
+
+replace: (
|
|
42
|
+
loc: RawLocation,
|
|
43
|
+
onComplete?: Function,
|
|
44
|
+
onAbort?: Function
|
|
45
|
+
) => void
|
|
31
46
|
+ensureURL: (push?: boolean) => void
|
|
32
47
|
+getCurrentLocation: () => string
|
|
48
|
+
+setupListeners: Function
|
|
33
49
|
|
|
34
50
|
constructor (router: Router, base: ?string) {
|
|
35
51
|
this.router = router
|
|
@@ -41,6 +57,7 @@ export class History {
|
|
|
41
57
|
this.readyCbs = []
|
|
42
58
|
this.readyErrorCbs = []
|
|
43
59
|
this.errorCbs = []
|
|
60
|
+
this.listeners = []
|
|
44
61
|
}
|
|
45
62
|
|
|
46
63
|
listen (cb: Function) {
|
|
@@ -67,13 +84,27 @@ export class History {
|
|
|
67
84
|
onComplete?: Function,
|
|
68
85
|
onAbort?: Function
|
|
69
86
|
) {
|
|
70
|
-
|
|
87
|
+
let route
|
|
88
|
+
// catch redirect option
|
|
89
|
+
try {
|
|
90
|
+
route = this.router.match(location, this.current)
|
|
91
|
+
} catch (e) {
|
|
92
|
+
this.errorCbs.forEach(cb => {
|
|
93
|
+
cb(e)
|
|
94
|
+
})
|
|
95
|
+
// Exception should still be thrown
|
|
96
|
+
throw e
|
|
97
|
+
}
|
|
98
|
+
const prev = this.current
|
|
71
99
|
this.confirmTransition(
|
|
72
100
|
route,
|
|
73
101
|
() => {
|
|
74
102
|
this.updateRoute(route)
|
|
75
103
|
onComplete && onComplete(route)
|
|
76
104
|
this.ensureURL()
|
|
105
|
+
this.router.afterHooks.forEach(hook => {
|
|
106
|
+
hook && hook(route, prev)
|
|
107
|
+
})
|
|
77
108
|
|
|
78
109
|
// fire ready cbs once
|
|
79
110
|
if (!this.ready) {
|
|
@@ -88,10 +119,14 @@ export class History {
|
|
|
88
119
|
onAbort(err)
|
|
89
120
|
}
|
|
90
121
|
if (err && !this.ready) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
122
|
+
// Initial redirection should not mark the history as ready yet
|
|
123
|
+
// because it's triggered by the redirection instead
|
|
124
|
+
if (!isNavigationFailure(err, NavigationFailureType.redirected) || prev !== START) {
|
|
125
|
+
this.ready = true
|
|
126
|
+
this.readyErrorCbs.forEach(cb => {
|
|
127
|
+
cb(err)
|
|
128
|
+
})
|
|
129
|
+
}
|
|
95
130
|
}
|
|
96
131
|
}
|
|
97
132
|
)
|
|
@@ -99,29 +134,37 @@ export class History {
|
|
|
99
134
|
|
|
100
135
|
confirmTransition (route: Route, onComplete: Function, onAbort?: Function) {
|
|
101
136
|
const current = this.current
|
|
137
|
+
this.pending = route
|
|
102
138
|
const abort = err => {
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
if (!isExtendedError(NavigationDuplicated, err) && isError(err)) {
|
|
139
|
+
// changed after adding errors
|
|
140
|
+
// before that change, redirect and aborted navigation would produce an err == null
|
|
141
|
+
if (!isNavigationFailure(err) && isError(err)) {
|
|
107
142
|
if (this.errorCbs.length) {
|
|
108
143
|
this.errorCbs.forEach(cb => {
|
|
109
144
|
cb(err)
|
|
110
145
|
})
|
|
111
146
|
} else {
|
|
112
|
-
|
|
147
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
148
|
+
warn(false, 'uncaught error during route navigation:')
|
|
149
|
+
}
|
|
113
150
|
console.error(err)
|
|
114
151
|
}
|
|
115
152
|
}
|
|
116
153
|
onAbort && onAbort(err)
|
|
117
154
|
}
|
|
155
|
+
const lastRouteIndex = route.matched.length - 1
|
|
156
|
+
const lastCurrentIndex = current.matched.length - 1
|
|
118
157
|
if (
|
|
119
158
|
isSameRoute(route, current) &&
|
|
120
159
|
// in the case the route map has been dynamically appended to
|
|
121
|
-
|
|
160
|
+
lastRouteIndex === lastCurrentIndex &&
|
|
161
|
+
route.matched[lastRouteIndex] === current.matched[lastCurrentIndex]
|
|
122
162
|
) {
|
|
123
163
|
this.ensureURL()
|
|
124
|
-
|
|
164
|
+
if (route.hash) {
|
|
165
|
+
handleScroll(this.router, current, route, false)
|
|
166
|
+
}
|
|
167
|
+
return abort(createNavigationDuplicatedError(current, route))
|
|
125
168
|
}
|
|
126
169
|
|
|
127
170
|
const { updated, deactivated, activated } = resolveQueue(
|
|
@@ -142,15 +185,17 @@ export class History {
|
|
|
142
185
|
resolveAsyncComponents(activated)
|
|
143
186
|
)
|
|
144
187
|
|
|
145
|
-
this.pending = route
|
|
146
188
|
const iterator = (hook: NavigationGuard, next) => {
|
|
147
189
|
if (this.pending !== route) {
|
|
148
|
-
return abort()
|
|
190
|
+
return abort(createNavigationCancelledError(current, route))
|
|
149
191
|
}
|
|
150
192
|
try {
|
|
151
193
|
hook(route, current, (to: any) => {
|
|
152
|
-
if (to === false
|
|
194
|
+
if (to === false) {
|
|
153
195
|
// next(false) -> abort navigation, ensure current URL
|
|
196
|
+
this.ensureURL(true)
|
|
197
|
+
abort(createNavigationAbortedError(current, route))
|
|
198
|
+
} else if (isError(to)) {
|
|
154
199
|
this.ensureURL(true)
|
|
155
200
|
abort(to)
|
|
156
201
|
} else if (
|
|
@@ -159,7 +204,7 @@ export class History {
|
|
|
159
204
|
(typeof to.path === 'string' || typeof to.name === 'string'))
|
|
160
205
|
) {
|
|
161
206
|
// next('/') or next({ path: '/' }) -> redirect
|
|
162
|
-
abort()
|
|
207
|
+
abort(createNavigationRedirectedError(current, route))
|
|
163
208
|
if (typeof to === 'object' && to.replace) {
|
|
164
209
|
this.replace(to)
|
|
165
210
|
} else {
|
|
@@ -176,23 +221,19 @@ export class History {
|
|
|
176
221
|
}
|
|
177
222
|
|
|
178
223
|
runQueue(queue, iterator, () => {
|
|
179
|
-
const postEnterCbs = []
|
|
180
|
-
const isValid = () => this.current === route
|
|
181
224
|
// wait until async components are resolved before
|
|
182
225
|
// extracting in-component enter guards
|
|
183
|
-
const enterGuards = extractEnterGuards(activated
|
|
226
|
+
const enterGuards = extractEnterGuards(activated)
|
|
184
227
|
const queue = enterGuards.concat(this.router.resolveHooks)
|
|
185
228
|
runQueue(queue, iterator, () => {
|
|
186
229
|
if (this.pending !== route) {
|
|
187
|
-
return abort()
|
|
230
|
+
return abort(createNavigationCancelledError(current, route))
|
|
188
231
|
}
|
|
189
232
|
this.pending = null
|
|
190
233
|
onComplete(route)
|
|
191
234
|
if (this.router.app) {
|
|
192
235
|
this.router.app.$nextTick(() => {
|
|
193
|
-
|
|
194
|
-
cb()
|
|
195
|
-
})
|
|
236
|
+
handleRouteEntered(route)
|
|
196
237
|
})
|
|
197
238
|
}
|
|
198
239
|
})
|
|
@@ -200,12 +241,24 @@ export class History {
|
|
|
200
241
|
}
|
|
201
242
|
|
|
202
243
|
updateRoute (route: Route) {
|
|
203
|
-
const prev = this.current
|
|
204
244
|
this.current = route
|
|
205
245
|
this.cb && this.cb(route)
|
|
206
|
-
|
|
207
|
-
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
setupListeners () {
|
|
249
|
+
// Default implementation is empty
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
teardown () {
|
|
253
|
+
// clean up event listeners
|
|
254
|
+
this.listeners.forEach(cleanupListener => {
|
|
255
|
+
cleanupListener()
|
|
208
256
|
})
|
|
257
|
+
this.listeners = []
|
|
258
|
+
|
|
259
|
+
// reset current history route
|
|
260
|
+
this.current = START
|
|
261
|
+
this.pending = null
|
|
209
262
|
}
|
|
210
263
|
}
|
|
211
264
|
|
|
@@ -296,15 +349,13 @@ function bindGuard (guard: NavigationGuard, instance: ?_Kdu): ?NavigationGuard {
|
|
|
296
349
|
}
|
|
297
350
|
|
|
298
351
|
function extractEnterGuards (
|
|
299
|
-
activated: Array<RouteRecord
|
|
300
|
-
cbs: Array<Function>,
|
|
301
|
-
isValid: () => boolean
|
|
352
|
+
activated: Array<RouteRecord>
|
|
302
353
|
): Array<?Function> {
|
|
303
354
|
return extractGuards(
|
|
304
355
|
activated,
|
|
305
356
|
'beforeRouteEnter',
|
|
306
357
|
(guard, _, match, key) => {
|
|
307
|
-
return bindEnterGuard(guard, match, key
|
|
358
|
+
return bindEnterGuard(guard, match, key)
|
|
308
359
|
}
|
|
309
360
|
)
|
|
310
361
|
}
|
|
@@ -312,41 +363,17 @@ function extractEnterGuards (
|
|
|
312
363
|
function bindEnterGuard (
|
|
313
364
|
guard: NavigationGuard,
|
|
314
365
|
match: RouteRecord,
|
|
315
|
-
key: string
|
|
316
|
-
cbs: Array<Function>,
|
|
317
|
-
isValid: () => boolean
|
|
366
|
+
key: string
|
|
318
367
|
): NavigationGuard {
|
|
319
368
|
return function routeEnterGuard (to, from, next) {
|
|
320
369
|
return guard(to, from, cb => {
|
|
321
370
|
if (typeof cb === 'function') {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
// we will need to poll for registration until current route
|
|
327
|
-
// is no longer valid.
|
|
328
|
-
poll(cb, match.instances, key, isValid)
|
|
329
|
-
})
|
|
371
|
+
if (!match.enteredCbs[key]) {
|
|
372
|
+
match.enteredCbs[key] = []
|
|
373
|
+
}
|
|
374
|
+
match.enteredCbs[key].push(cb)
|
|
330
375
|
}
|
|
331
376
|
next(cb)
|
|
332
377
|
})
|
|
333
378
|
}
|
|
334
379
|
}
|
|
335
|
-
|
|
336
|
-
function poll (
|
|
337
|
-
cb: any, // somehow flow cannot infer this is a function
|
|
338
|
-
instances: Object,
|
|
339
|
-
key: string,
|
|
340
|
-
isValid: () => boolean
|
|
341
|
-
) {
|
|
342
|
-
if (
|
|
343
|
-
instances[key] &&
|
|
344
|
-
!instances[key]._isBeingDestroyed // do not reuse being destroyed instance
|
|
345
|
-
) {
|
|
346
|
-
cb(instances[key])
|
|
347
|
-
} else if (isValid()) {
|
|
348
|
-
setTimeout(() => {
|
|
349
|
-
poll(cb, instances, key, isValid)
|
|
350
|
-
}, 16)
|
|
351
|
-
}
|
|
352
|
-
}
|
package/src/history/hash.js
CHANGED
|
@@ -20,31 +20,40 @@ export class HashHistory extends History {
|
|
|
20
20
|
// this is delayed until the app mounts
|
|
21
21
|
// to avoid the hashchange listener being fired too early
|
|
22
22
|
setupListeners () {
|
|
23
|
+
if (this.listeners.length > 0) {
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
23
27
|
const router = this.router
|
|
24
28
|
const expectScroll = router.options.scrollBehavior
|
|
25
29
|
const supportsScroll = supportsPushState && expectScroll
|
|
26
30
|
|
|
27
31
|
if (supportsScroll) {
|
|
28
|
-
setupScroll()
|
|
32
|
+
this.listeners.push(setupScroll())
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
()
|
|
34
|
-
|
|
35
|
-
if (!ensureSlash()) {
|
|
36
|
-
return
|
|
37
|
-
}
|
|
38
|
-
this.transitionTo(getHash(), route => {
|
|
39
|
-
if (supportsScroll) {
|
|
40
|
-
handleScroll(this.router, route, current, true)
|
|
41
|
-
}
|
|
42
|
-
if (!supportsPushState) {
|
|
43
|
-
replaceHash(route.fullPath)
|
|
44
|
-
}
|
|
45
|
-
})
|
|
35
|
+
const handleRoutingEvent = () => {
|
|
36
|
+
const current = this.current
|
|
37
|
+
if (!ensureSlash()) {
|
|
38
|
+
return
|
|
46
39
|
}
|
|
40
|
+
this.transitionTo(getHash(), route => {
|
|
41
|
+
if (supportsScroll) {
|
|
42
|
+
handleScroll(this.router, route, current, true)
|
|
43
|
+
}
|
|
44
|
+
if (!supportsPushState) {
|
|
45
|
+
replaceHash(route.fullPath)
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
const eventType = supportsPushState ? 'popstate' : 'hashchange'
|
|
50
|
+
window.addEventListener(
|
|
51
|
+
eventType,
|
|
52
|
+
handleRoutingEvent
|
|
47
53
|
)
|
|
54
|
+
this.listeners.push(() => {
|
|
55
|
+
window.removeEventListener(eventType, handleRoutingEvent)
|
|
56
|
+
})
|
|
48
57
|
}
|
|
49
58
|
|
|
50
59
|
push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
|
|
@@ -115,17 +124,6 @@ export function getHash (): string {
|
|
|
115
124
|
if (index < 0) return ''
|
|
116
125
|
|
|
117
126
|
href = href.slice(index + 1)
|
|
118
|
-
// decode the hash but not the search or hash
|
|
119
|
-
// as search(query) is already decoded
|
|
120
|
-
const searchIndex = href.indexOf('?')
|
|
121
|
-
if (searchIndex < 0) {
|
|
122
|
-
const hashIndex = href.indexOf('#')
|
|
123
|
-
if (hashIndex > -1) {
|
|
124
|
-
href = decodeURI(href.slice(0, hashIndex)) + href.slice(hashIndex)
|
|
125
|
-
} else href = decodeURI(href)
|
|
126
|
-
} else {
|
|
127
|
-
href = decodeURI(href.slice(0, searchIndex)) + href.slice(searchIndex)
|
|
128
|
-
}
|
|
129
127
|
|
|
130
128
|
return href
|
|
131
129
|
}
|
package/src/history/html5.js
CHANGED
|
@@ -8,24 +8,34 @@ import { setupScroll, handleScroll } from '../util/scroll'
|
|
|
8
8
|
import { pushState, replaceState, supportsPushState } from '../util/push-state'
|
|
9
9
|
|
|
10
10
|
export class HTML5History extends History {
|
|
11
|
+
_startLocation: string
|
|
12
|
+
|
|
11
13
|
constructor (router: Router, base: ?string) {
|
|
12
14
|
super(router, base)
|
|
13
15
|
|
|
16
|
+
this._startLocation = getLocation(this.base)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
setupListeners () {
|
|
20
|
+
if (this.listeners.length > 0) {
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const router = this.router
|
|
14
25
|
const expectScroll = router.options.scrollBehavior
|
|
15
26
|
const supportsScroll = supportsPushState && expectScroll
|
|
16
27
|
|
|
17
28
|
if (supportsScroll) {
|
|
18
|
-
setupScroll()
|
|
29
|
+
this.listeners.push(setupScroll())
|
|
19
30
|
}
|
|
20
31
|
|
|
21
|
-
const
|
|
22
|
-
window.addEventListener('popstate', e => {
|
|
32
|
+
const handleRoutingEvent = () => {
|
|
23
33
|
const current = this.current
|
|
24
34
|
|
|
25
35
|
// Avoiding first `popstate` event dispatched in some browsers but first
|
|
26
36
|
// history route not updated since async guard at the same time.
|
|
27
37
|
const location = getLocation(this.base)
|
|
28
|
-
if (this.current === START && location ===
|
|
38
|
+
if (this.current === START && location === this._startLocation) {
|
|
29
39
|
return
|
|
30
40
|
}
|
|
31
41
|
|
|
@@ -34,6 +44,10 @@ export class HTML5History extends History {
|
|
|
34
44
|
handleScroll(router, route, current, true)
|
|
35
45
|
}
|
|
36
46
|
})
|
|
47
|
+
}
|
|
48
|
+
window.addEventListener('popstate', handleRoutingEvent)
|
|
49
|
+
this.listeners.push(() => {
|
|
50
|
+
window.removeEventListener('popstate', handleRoutingEvent)
|
|
37
51
|
})
|
|
38
52
|
}
|
|
39
53
|
|
|
@@ -72,8 +86,13 @@ export class HTML5History extends History {
|
|
|
72
86
|
}
|
|
73
87
|
|
|
74
88
|
export function getLocation (base: string): string {
|
|
75
|
-
let path =
|
|
76
|
-
|
|
89
|
+
let path = window.location.pathname
|
|
90
|
+
const pathLowerCase = path.toLowerCase()
|
|
91
|
+
const baseLowerCase = base.toLowerCase()
|
|
92
|
+
// base="/a" shouldn't turn path="/app" into "/a/pp"
|
|
93
|
+
// so we ensure the trailing slash in the base
|
|
94
|
+
if (base && ((pathLowerCase === baseLowerCase) ||
|
|
95
|
+
(pathLowerCase.indexOf(cleanPath(baseLowerCase + '/')) === 0))) {
|
|
77
96
|
path = path.slice(base.length)
|
|
78
97
|
}
|
|
79
98
|
return (path || '/') + window.location.search + window.location.hash
|
package/src/index.js
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import { install } from './install'
|
|
4
4
|
import { START } from './util/route'
|
|
5
|
-
import { assert } from './util/warn'
|
|
5
|
+
import { assert, warn } from './util/warn'
|
|
6
6
|
import { inBrowser } from './util/dom'
|
|
7
7
|
import { cleanPath } from './util/path'
|
|
8
8
|
import { createMatcher } from './create-matcher'
|
|
9
9
|
import { normalizeLocation } from './util/location'
|
|
10
10
|
import { supportsPushState } from './util/push-state'
|
|
11
|
+
import { handleScroll } from './util/scroll'
|
|
11
12
|
|
|
12
13
|
import { HashHistory } from './history/hash'
|
|
13
14
|
import { HTML5History } from './history/html5'
|
|
@@ -15,24 +16,32 @@ import { AbstractHistory } from './history/abstract'
|
|
|
15
16
|
|
|
16
17
|
import type { Matcher } from './create-matcher'
|
|
17
18
|
|
|
19
|
+
import { isNavigationFailure, NavigationFailureType } from './util/errors'
|
|
20
|
+
|
|
18
21
|
export default class KduRouter {
|
|
19
|
-
static install: () => void
|
|
20
|
-
static version: string
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
22
|
+
static install: () => void
|
|
23
|
+
static version: string
|
|
24
|
+
static isNavigationFailure: Function
|
|
25
|
+
static NavigationFailureType: any
|
|
26
|
+
static START_LOCATION: Route
|
|
27
|
+
|
|
28
|
+
app: any
|
|
29
|
+
apps: Array<any>
|
|
30
|
+
ready: boolean
|
|
31
|
+
readyCbs: Array<Function>
|
|
32
|
+
options: RouterOptions
|
|
33
|
+
mode: string
|
|
34
|
+
history: HashHistory | HTML5History | AbstractHistory
|
|
35
|
+
matcher: Matcher
|
|
36
|
+
fallback: boolean
|
|
37
|
+
beforeHooks: Array<?NavigationGuard>
|
|
38
|
+
resolveHooks: Array<?NavigationGuard>
|
|
39
|
+
afterHooks: Array<?AfterNavigationHook>
|
|
34
40
|
|
|
35
41
|
constructor (options: RouterOptions = {}) {
|
|
42
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
43
|
+
warn(this instanceof KduRouter, `Router must be called with the new operator.`)
|
|
44
|
+
}
|
|
36
45
|
this.app = null
|
|
37
46
|
this.apps = []
|
|
38
47
|
this.options = options
|
|
@@ -42,7 +51,8 @@ export default class KduRouter {
|
|
|
42
51
|
this.matcher = createMatcher(options.routes || [], this)
|
|
43
52
|
|
|
44
53
|
let mode = options.mode || 'hash'
|
|
45
|
-
this.fallback =
|
|
54
|
+
this.fallback =
|
|
55
|
+
mode === 'history' && !supportsPushState && options.fallback !== false
|
|
46
56
|
if (this.fallback) {
|
|
47
57
|
mode = 'hash'
|
|
48
58
|
}
|
|
@@ -68,11 +78,7 @@ export default class KduRouter {
|
|
|
68
78
|
}
|
|
69
79
|
}
|
|
70
80
|
|
|
71
|
-
match (
|
|
72
|
-
raw: RawLocation,
|
|
73
|
-
current?: Route,
|
|
74
|
-
redirectedFrom?: Location
|
|
75
|
-
): Route {
|
|
81
|
+
match (raw: RawLocation, current?: Route, redirectedFrom?: Location): Route {
|
|
76
82
|
return this.matcher.match(raw, current, redirectedFrom)
|
|
77
83
|
}
|
|
78
84
|
|
|
@@ -81,11 +87,12 @@ export default class KduRouter {
|
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
init (app: any /* Kdu component instance */) {
|
|
84
|
-
process.env.NODE_ENV !== 'production' &&
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
process.env.NODE_ENV !== 'production' &&
|
|
91
|
+
assert(
|
|
92
|
+
install.installed,
|
|
93
|
+
`not installed. Make sure to call \`Kdu.use(KduRouter)\` ` +
|
|
94
|
+
`before creating root instance.`
|
|
95
|
+
)
|
|
89
96
|
|
|
90
97
|
this.apps.push(app)
|
|
91
98
|
|
|
@@ -97,6 +104,8 @@ export default class KduRouter {
|
|
|
97
104
|
// ensure we still have a main app or null if no apps
|
|
98
105
|
// we do not release the router so it can be reused
|
|
99
106
|
if (this.app === app) this.app = this.apps[0] || null
|
|
107
|
+
|
|
108
|
+
if (!this.app) this.history.teardown()
|
|
100
109
|
})
|
|
101
110
|
|
|
102
111
|
// main app previously initialized
|
|
@@ -109,21 +118,29 @@ export default class KduRouter {
|
|
|
109
118
|
|
|
110
119
|
const history = this.history
|
|
111
120
|
|
|
112
|
-
if (history instanceof HTML5History) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
121
|
+
if (history instanceof HTML5History || history instanceof HashHistory) {
|
|
122
|
+
const handleInitialScroll = routeOrError => {
|
|
123
|
+
const from = history.current
|
|
124
|
+
const expectScroll = this.options.scrollBehavior
|
|
125
|
+
const supportsScroll = supportsPushState && expectScroll
|
|
126
|
+
|
|
127
|
+
if (supportsScroll && 'fullPath' in routeOrError) {
|
|
128
|
+
handleScroll(this, routeOrError, from, false)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const setupListeners = routeOrError => {
|
|
116
132
|
history.setupListeners()
|
|
133
|
+
handleInitialScroll(routeOrError)
|
|
117
134
|
}
|
|
118
135
|
history.transitionTo(
|
|
119
136
|
history.getCurrentLocation(),
|
|
120
|
-
|
|
121
|
-
|
|
137
|
+
setupListeners,
|
|
138
|
+
setupListeners
|
|
122
139
|
)
|
|
123
140
|
}
|
|
124
141
|
|
|
125
142
|
history.listen(route => {
|
|
126
|
-
this.apps.forEach(
|
|
143
|
+
this.apps.forEach(app => {
|
|
127
144
|
app._route = route
|
|
128
145
|
})
|
|
129
146
|
})
|
|
@@ -192,11 +209,14 @@ export default class KduRouter {
|
|
|
192
209
|
if (!route) {
|
|
193
210
|
return []
|
|
194
211
|
}
|
|
195
|
-
return [].concat.apply(
|
|
196
|
-
|
|
197
|
-
|
|
212
|
+
return [].concat.apply(
|
|
213
|
+
[],
|
|
214
|
+
route.matched.map(m => {
|
|
215
|
+
return Object.keys(m.components).map(key => {
|
|
216
|
+
return m.components[key]
|
|
217
|
+
})
|
|
198
218
|
})
|
|
199
|
-
|
|
219
|
+
)
|
|
200
220
|
}
|
|
201
221
|
|
|
202
222
|
resolve (
|
|
@@ -212,12 +232,7 @@ export default class KduRouter {
|
|
|
212
232
|
resolved: Route
|
|
213
233
|
} {
|
|
214
234
|
current = current || this.history.current
|
|
215
|
-
const location = normalizeLocation(
|
|
216
|
-
to,
|
|
217
|
-
current,
|
|
218
|
-
append,
|
|
219
|
-
this
|
|
220
|
-
)
|
|
235
|
+
const location = normalizeLocation(to, current, append, this)
|
|
221
236
|
const route = this.match(location, current)
|
|
222
237
|
const fullPath = route.redirectedFrom || route.fullPath
|
|
223
238
|
const base = this.history.base
|
|
@@ -232,7 +247,21 @@ export default class KduRouter {
|
|
|
232
247
|
}
|
|
233
248
|
}
|
|
234
249
|
|
|
250
|
+
getRoutes () {
|
|
251
|
+
return this.matcher.getRoutes()
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
addRoute (parentOrRoute: string | RouteConfig, route?: RouteConfig) {
|
|
255
|
+
this.matcher.addRoute(parentOrRoute, route)
|
|
256
|
+
if (this.history.current !== START) {
|
|
257
|
+
this.history.transitionTo(this.history.getCurrentLocation())
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
235
261
|
addRoutes (routes: Array<RouteConfig>) {
|
|
262
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
263
|
+
warn(false, 'router.addRoutes() is deprecated and has been removed in Kdu Router 4. Use router.addRoute() instead.')
|
|
264
|
+
}
|
|
236
265
|
this.matcher.addRoutes(routes)
|
|
237
266
|
if (this.history.current !== START) {
|
|
238
267
|
this.history.transitionTo(this.history.getCurrentLocation())
|
|
@@ -255,6 +284,9 @@ function createHref (base: string, fullPath: string, mode) {
|
|
|
255
284
|
|
|
256
285
|
KduRouter.install = install
|
|
257
286
|
KduRouter.version = '__VERSION__'
|
|
287
|
+
KduRouter.isNavigationFailure = isNavigationFailure
|
|
288
|
+
KduRouter.NavigationFailureType = NavigationFailureType
|
|
289
|
+
KduRouter.START_LOCATION = START
|
|
258
290
|
|
|
259
291
|
if (inBrowser && window.Kdu) {
|
|
260
292
|
window.Kdu.use(KduRouter)
|