1 |
|
2 |
|
3 | import { install } from './install'
|
4 | import { START } from './util/route'
|
5 | import { assert } from './util/warn'
|
6 | import { inBrowser } from './util/dom'
|
7 | import { cleanPath } from './util/path'
|
8 | import { createMatcher } from './create-matcher'
|
9 | import { normalizeLocation } from './util/location'
|
10 | import { supportsPushState } from './util/push-state'
|
11 |
|
12 | import { HashHistory } from './history/hash'
|
13 | import { HTML5History } from './history/html5'
|
14 | import { AbstractHistory } from './history/abstract'
|
15 |
|
16 | import type { Matcher } from './create-matcher'
|
17 |
|
18 | export 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 ) {
|
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 |
|
93 |
|
94 | app.$once('hook:destroyed', () => {
|
95 |
|
96 | const index = this.apps.indexOf(app)
|
97 | if (index > -1) this.apps.splice(index, 1)
|
98 |
|
99 |
|
100 | if (this.app === app) this.app = this.apps[0] || null
|
101 | })
|
102 |
|
103 |
|
104 |
|
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 |
|
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 |
|
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 |
|
230 | function 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 |
|
238 | function createHref (base: string, fullPath: string, mode) {
|
239 | var path = mode === 'hash' ? '#' + fullPath : fullPath
|
240 | return base ? cleanPath(base + '/' + path) : path
|
241 | }
|
242 |
|
243 | VueRouter.install = install
|
244 | VueRouter.version = '__VERSION__'
|
245 |
|
246 | if (inBrowser && window.Vue) {
|
247 | window.Vue.use(VueRouter)
|
248 | }
|