- VueRouter 对象
- 总结
- 总结
VueRouter 对象
VueRouter 的实现是一个类,我们先对它做一个简单地分析,它的定义在 src/index.js 中:
export default class VueRouter {static install: () => void;static version: string;app: any;apps: Array<any>;ready: boolean;readyCbs: Array<Function>;options: RouterOptions;mode: string;history: HashHistory | HTML5History | AbstractHistory;matcher: Matcher;fallback: boolean;beforeHooks: Array<?NavigationGuard>;resolveHooks: Array<?NavigationGuard>;afterHooks: Array<?AfterNavigationHook>;constructor (options: RouterOptions = {}) {this.app = nullthis.apps = []this.options = optionsthis.beforeHooks = []this.resolveHooks = []this.afterHooks = []this.matcher = createMatcher(options.routes || [], this)let mode = options.mode || 'hash'this.fallback = mode === 'history' && !supportsPushState && options.fallback !== falseif (this.fallback) {mode = 'hash'}if (!inBrowser) {mode = 'abstract'}this.mode = modeswitch (mode) {case 'history':this.history = new HTML5History(this, options.base)breakcase 'hash':this.history = new HashHistory(this, options.base, this.fallback)breakcase 'abstract':this.history = new AbstractHistory(this, options.base)breakdefault:if (process.env.NODE_ENV !== 'production') {assert(false, `invalid mode: ${mode}`)}}}match (raw: RawLocation,current?: Route,redirectedFrom?: Location): Route {return this.matcher.match(raw, current, redirectedFrom)}get currentRoute (): ?Route {return this.history && this.history.current}init (app: any) {process.env.NODE_ENV !== 'production' && assert(install.installed,`not installed. Make sure to call \`Vue.use(VueRouter)\` ` +`before creating root instance.`)this.apps.push(app)if (this.app) {return}this.app = appconst history = this.historyif (history instanceof HTML5History) {history.transitionTo(history.getCurrentLocation())} else if (history instanceof HashHistory) {const setupHashListener = () => {history.setupListeners()}history.transitionTo(history.getCurrentLocation(),setupHashListener,setupHashListener)}history.listen(route => {this.apps.forEach((app) => {app._route = route})})}beforeEach (fn: Function): Function {return registerHook(this.beforeHooks, fn)}beforeResolve (fn: Function): Function {return registerHook(this.resolveHooks, fn)}afterEach (fn: Function): Function {return registerHook(this.afterHooks, fn)}onReady (cb: Function, errorCb?: Function) {this.history.onReady(cb, errorCb)}onError (errorCb: Function) {this.history.onError(errorCb)}push (location: RawLocation, onComplete?: Function, onAbort?: Function) {this.history.push(location, onComplete, onAbort)}replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {this.history.replace(location, onComplete, onAbort)}go (n: number) {this.history.go(n)}back () {this.go(-1)}forward () {this.go(1)}getMatchedComponents (to?: RawLocation | Route): Array<any> {const route: any = to? to.matched? to: this.resolve(to).route: this.currentRouteif (!route) {return []}return [].concat.apply([], route.matched.map(m => {return Object.keys(m.components).map(key => {return m.components[key]})}))}resolve (to: RawLocation,current?: Route,append?: boolean): {location: Location,route: Route,href: string,normalizedTo: Location,resolved: Route} {const location = normalizeLocation(to,current || this.history.current,append,this)const route = this.match(location, current)const fullPath = route.redirectedFrom || route.fullPathconst base = this.history.baseconst href = createHref(base, fullPath, this.mode)return {location,route,href,normalizedTo: location,resolved: route}}addRoutes (routes: Array<RouteConfig>) {this.matcher.addRoutes(routes)if (this.history.current !== START) {this.history.transitionTo(this.history.getCurrentLocation())}}}
VueRouter 定义了一些属性和方法,我们先从它的构造函数看,当我们执行 new VueRouter 的时候做了哪些事情。
constructor (options: RouterOptions = {}) {this.app = nullthis.apps = []this.options = optionsthis.beforeHooks = []this.resolveHooks = []this.afterHooks = []this.matcher = createMatcher(options.routes || [], this)let mode = options.mode || 'hash'this.fallback = mode === 'history' && !supportsPushState && options.fallback !== falseif (this.fallback) {mode = 'hash'}if (!inBrowser) {mode = 'abstract'}this.mode = modeswitch (mode) {case 'history':this.history = new HTML5History(this, options.base)breakcase 'hash':this.history = new HashHistory(this, options.base, this.fallback)breakcase 'abstract':this.history = new AbstractHistory(this, options.base)breakdefault:if (process.env.NODE_ENV !== 'production') {assert(false, `invalid mode: ${mode}`)}}}
构造函数定义了一些属性,其中 this.app 表示根 Vue 实例,this.apps 保存持有 $options.router 属性的 Vue 实例,this.options 保存传入的路由配置,this.beforeHooks、this.resolveHooks、this.afterHooks 表示一些钩子函数,我们之后会介绍,this.matcher 表示路由匹配器,我们之后会介绍,this.fallback 表示在浏览器不支持 history.pushState 的情况下,根据传入的 fallback 配置参数,决定是否回退到hash模式,this.mode 表示路由创建的模式,this.history 表示路由历史的具体的实现实例,它是根据 this.mode 的不同实现不同,它有 History 基类,然后不同的 history 实现都是继承 History。
实例化 VueRouter 后会返回它的实例 router,我们在 new Vue 的时候会把 router 作为配置的属性传入,回顾一下上一节我们讲 beforeCreate 混入的时候有这么一段代码:
beforeCreate() {if (isDef(this.$options.router)) {// ...this._router = this.$options.routerthis._router.init(this)// ...}}
所以组件在执行 beforeCreate 钩子函数的时候,如果传入了 router 实例,都会执行 router.init 方法:
init (app: any) {process.env.NODE_ENV !== 'production' && assert(install.installed,`not installed. Make sure to call \`Vue.use(VueRouter)\` ` +`before creating root instance.`)this.apps.push(app)if (this.app) {return}this.app = appconst history = this.historyif (history instanceof HTML5History) {history.transitionTo(history.getCurrentLocation())} else if (history instanceof HashHistory) {const setupHashListener = () => {history.setupListeners()}history.transitionTo(history.getCurrentLocation(),setupHashListener,setupHashListener)}history.listen(route => {this.apps.forEach((app) => {app._route = route})})}
init 的逻辑很简单,它传入的参数是 Vue 实例,然后存储到 this.apps 中;只有根 Vue 实例会保存到 this.app 中,并且会拿到当前的 this.history,根据它的不同类型来执行不同逻辑,由于我们平时使用 hash 路由多一些,所以我们先看这部分逻辑,先定义了 setupHashListener 函数,接着执行了 history.transitionTo 方法,它是定义在 History 基类中,代码在 src/history/base.js:
transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {const route = this.router.match(location, this.current)// ...}
我们先不着急去看 transitionTo 的具体实现,先看第一行代码,它调用了 this.router.match 函数:
match (raw: RawLocation,current?: Route,redirectedFrom?: Location): Route {return this.matcher.match(raw, current, redirectedFrom)}
实际上是调用了 this.matcher.match 方法去做匹配,所以接下来我们先来了解一下 matcher 的相关实现。
总结
通过这一节的分析,我们大致对 VueRouter 类有了大致了解,知道了它的一些属性和方法,同时了了解到在组件的初始化阶段,执行到 beforeCreate 钩子函数的时候会执行 router.init 方法,然后又会执行 history.transitionTo 方法做路由过渡,进而引出了 matcher 的概念,接下来我们先研究一下 matcher 的相关实现。
原文: https://ustbhuangyi.github.io/vue-analysis/vue-router/router.html
