UNPKG

6.19 kBJavaScriptView Raw
1/* @flow */
2
3import { install } from './install'
4import { START } from './util/route'
5import { assert } from './util/warn'
6import { inBrowser } from './util/dom'
7import { cleanPath } from './util/path'
8import { createMatcher } from './create-matcher'
9import { normalizeLocation } from './util/location'
10import { supportsPushState } from './util/push-state'
11
12import { HashHistory } from './history/hash'
13import { HTML5History } from './history/html5'
14import { AbstractHistory } from './history/abstract'
15
16import type { Matcher } from './create-matcher'
17
18export default class VueRouter {
19 static install: () => void;
20 static version: string;
21
22 app: any;
23 apps: Array<any>;
24 ready: boolean;
25 readyCbs: Array<Function>;
26 options: RouterOptions;
27 mode: string;
28 history: HashHistory | HTML5History | AbstractHistory;
29 matcher: Matcher;
30 fallback: boolean;
31 beforeHooks: Array<?NavigationGuard>;
32 resolveHooks: Array<?NavigationGuard>;
33 afterHooks: Array<?AfterNavigationHook>;
34
35 constructor (options: RouterOptions = {}) {
36 this.app = null
37 this.apps = []
38 this.options = options
39 this.beforeHooks = []
40 this.resolveHooks = []
41 this.afterHooks = []
42 this.matcher = createMatcher(options.routes || [], this)
43
44 let mode = options.mode || 'hash'
45 this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
46 if (this.fallback) {
47 mode = 'hash'
48 }
49 if (!inBrowser) {
50 mode = 'abstract'
51 }
52 this.mode = mode
53
54 switch (mode) {
55 case 'history':
56 this.history = new HTML5History(this, options.base)
57 break
58 case 'hash':
59 this.history = new HashHistory(this, options.base, this.fallback)
60 break
61 case 'abstract':
62 this.history = new AbstractHistory(this, options.base)
63 break
64 default:
65 if (process.env.NODE_ENV !== 'production') {
66 assert(false, `invalid mode: ${mode}`)
67 }
68 }
69 }
70
71 match (
72 raw: RawLocation,
73 current?: Route,
74 redirectedFrom?: Location
75 ): Route {
76 return this.matcher.match(raw, current, redirectedFrom)
77 }
78
79 get currentRoute (): ?Route {
80 return this.history && this.history.current
81 }
82
83 init (app: any /* Vue component instance */) {
84 process.env.NODE_ENV !== 'production' && assert(
85 install.installed,
86 `not installed. Make sure to call \`Vue.use(VueRouter)\` ` +
87 `before creating root instance.`
88 )
89
90 this.apps.push(app)
91
92 // set up app destroyed handler
93 // https://github.com/vuejs/vue-router/issues/2639
94 app.$once('hook:destroyed', () => {
95 // clean out app from this.apps array once destroyed
96 const index = this.apps.indexOf(app)
97 if (index > -1) this.apps.splice(index, 1)
98 // ensure we still have a main app or null if no apps
99 // we do not release the router so it can be reused
100 if (this.app === app) this.app = this.apps[0] || null
101 })
102
103 // main app previously initialized
104 // return as we don't need to set up new history listener
105 if (this.app) {
106 return
107 }
108
109 this.app = app
110
111 const history = this.history
112
113 if (history instanceof HTML5History) {
114 history.transitionTo(history.getCurrentLocation())
115 } else if (history instanceof HashHistory) {
116 const setupHashListener = () => {
117 history.setupListeners()
118 }
119 history.transitionTo(
120 history.getCurrentLocation(),
121 setupHashListener,
122 setupHashListener
123 )
124 }
125
126 history.listen(route => {
127 this.apps.forEach((app) => {
128 app._route = route
129 })
130 })
131 }
132
133 beforeEach (fn: Function): Function {
134 return registerHook(this.beforeHooks, fn)
135 }
136
137 beforeResolve (fn: Function): Function {
138 return registerHook(this.resolveHooks, fn)
139 }
140
141 afterEach (fn: Function): Function {
142 return registerHook(this.afterHooks, fn)
143 }
144
145 onReady (cb: Function, errorCb?: Function) {
146 this.history.onReady(cb, errorCb)
147 }
148
149 onError (errorCb: Function) {
150 this.history.onError(errorCb)
151 }
152
153 push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
154 this.history.push(location, onComplete, onAbort)
155 }
156
157 replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
158 this.history.replace(location, onComplete, onAbort)
159 }
160
161 go (n: number) {
162 this.history.go(n)
163 }
164
165 back () {
166 this.go(-1)
167 }
168
169 forward () {
170 this.go(1)
171 }
172
173 getMatchedComponents (to?: RawLocation | Route): Array<any> {
174 const route: any = to
175 ? to.matched
176 ? to
177 : this.resolve(to).route
178 : this.currentRoute
179 if (!route) {
180 return []
181 }
182 return [].concat.apply([], route.matched.map(m => {
183 return Object.keys(m.components).map(key => {
184 return m.components[key]
185 })
186 }))
187 }
188
189 resolve (
190 to: RawLocation,
191 current?: Route,
192 append?: boolean
193 ): {
194 location: Location,
195 route: Route,
196 href: string,
197 // for backwards compat
198 normalizedTo: Location,
199 resolved: Route
200 } {
201 current = current || this.history.current
202 const location = normalizeLocation(
203 to,
204 current,
205 append,
206 this
207 )
208 const route = this.match(location, current)
209 const fullPath = route.redirectedFrom || route.fullPath
210 const base = this.history.base
211 const href = createHref(base, fullPath, this.mode)
212 return {
213 location,
214 route,
215 href,
216 // for backwards compat
217 normalizedTo: location,
218 resolved: route
219 }
220 }
221
222 addRoutes (routes: Array<RouteConfig>) {
223 this.matcher.addRoutes(routes)
224 if (this.history.current !== START) {
225 this.history.transitionTo(this.history.getCurrentLocation())
226 }
227 }
228}
229
230function registerHook (list: Array<any>, fn: Function): Function {
231 list.push(fn)
232 return () => {
233 const i = list.indexOf(fn)
234 if (i > -1) list.splice(i, 1)
235 }
236}
237
238function createHref (base: string, fullPath: string, mode) {
239 var path = mode === 'hash' ? '#' + fullPath : fullPath
240 return base ? cleanPath(base + '/' + path) : path
241}
242
243VueRouter.install = install
244VueRouter.version = '__VERSION__'
245
246if (inBrowser && window.Vue) {
247 window.Vue.use(VueRouter)
248}