问题是这样的
当我访问本地项目localhost:8080/#/home/111
。
在Home组件中的钩子函数可以获取到正确的路径,然而在App.vue的mounted钩子函数中通过this.$route.path
获取到的值却是/
?
// router
const routes = [
{
path: '',
alias: '/home/:id',
name: 'Home',
component: () => import('@/views/Home.vue')
// component: Home 同步加载就不会有问题
}
]
// App.vue中
{
mounted () {
console.log('mounted', this.$route.path) // mounted /
}
}
组件的生命周期
在说这个问题之前,先要明白同步引入Home组件与异步引入是有区别的。
同步组件
- 父组件 created
- 子组件 created
- 子组件 mounted
- 父组件 mounted
异步组件
- 父组件 created、mounted
- 异步的子组件 created、mounted
vue-router
install
在通过vue.use(vueRouter)
注册插件时,会调用vueRouter
下的install
方法, 并把Vue作为参数传入,VurRouter
在一开始就会执行初始化。
这里面做了两件事情
- 为Vue的原型链中添加
$router
、$route
属性。以后在vue实例里面通过this.$router
实际上就是vueRouter
实例的router - 全局mixin钩子函数beforeCreate。 在这里会给
vueRoute
添加响应式属性_route
。
new vueRouter(route)
在这里面又做了两件重要的事情:
- 创建
matcher
。
会根据配置的route
生成不同的映射,包括pathMap
,nameMap
以及pathList
。暴露出了match
方法(解决了当前路径匹配到什么的问题)、addRoute
方法(动态添加路由,其实就是忘pathMap
、nameMap
添加了新的映射)。
- 创建
HashHistory
或者HTML5History
创建HashHistory
或者HTML5History
挂载在history下。
在HashHistory创建中当前路由默认是START: {fullPath: "/",hash: "",matched: [],meta: {},name: null,params: {},path: "/",query: {}}
。
beforeCreate阶段
会先执行vueRouter的init方法,并传入vue实例,第一次执行时会主动调用transitionTo方法,并且注册popstate/hashchange事件。
在transitionTo会调用match方法,得到了route。这个route就是路由钩子函数中的to参数。
此时vueRouter的current还是默认的START,接着调用confirmTransition
confirmTransition
confirmTransition里面主要是路由中的钩子函数做了处理:
const queue = [].concat(
// in-component leave guards
extractLeaveGuards(deactivated),
// global before hooks
this.router.beforeHooks,
// in-component update hooks
extractUpdateHooks(updated),
// in-config enter guards
activated.map(m => m.beforeEnter),
// async components
resolveAsyncComponents(activated)
)
这里queue类似express里面调用next来执行下一个queue任务,如果是异步函数,那么它会返回一个Promise,等到加载成功了再调用next。
因为异步组件的加载是异步的(这好像是废话),所以当vue实例调用created、mounted的时候,vueRouter的里面的next还没执行,vueRouter的状态还没有切换,history的current还是START。所以在组件App.vue
的mounted
钩子函数里面拿到的path
还是/
。
如果你是在组件里面去取值,这个异步组件肯定已经出来了,路由更新也已经完成,就能获取到正确的path。
动态路由加载完成
当动态路由加载完成时,才会调用next(),继续往下走(包括组件内的钩子、resolve钩子等等)。可以看到此时History模块还没完成切换:
history切换的那一步要等resolve等钩子函数走完,才会调用updateRoute
方法, this.currentRoute = route
。这一步执行完,history的current就替换为新的route了。
总结
路由匹配到异步组件时,组件的可能由于各种各样的原因(比如next('/login')
重定向了、或者网络原因加载不出来),vueRouter的history下的current要保证组件加载成功才会切换状态。
所以App.vue的mounted钩子只能获取到初始的route。如果非得在App.vue获取到动态路由的值,建议用watcher监听,不要在钩子函数里面去做与路由有关的初始化操作。