vue-router: 在App.vue中获取不到正确的路由?

10,684 阅读3分钟

问题是这样的

当我访问本地项目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组件与异步引入是有区别的。

同步组件

  1. 父组件 created
  2. 子组件 created
  3. 子组件 mounted
  4. 父组件 mounted

异步组件

  1. 父组件 created、mounted
  2. 异步的子组件 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)

在这里面又做了两件重要的事情:

  1. 创建matcher

会根据配置的route生成不同的映射,包括pathMap,nameMap以及pathList。暴露出了match方法(解决了当前路径匹配到什么的问题)、addRoute方法(动态添加路由,其实就是忘pathMapnameMap添加了新的映射)。

  1. 创建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.vuemounted钩子函数里面拿到的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监听,不要在钩子函数里面去做与路由有关的初始化操作。