Vue-Router V4 动态路由踩坑

1,995 阅读4分钟

Vue-Router全局路由钩子(导航守卫)使用

旧版的全局路由钩子的使用方法

router.beforeEach((to, from, next) => {
  if (isLogin() && to.name === 'Login') {
    next('/')
  } else if (!isLogin() && to.name !== 'Login') {
    next({ name: 'Login' })
  } else {
    next()
  }
})

新版全局路由钩子的使用方法

router.beforeEach((to, from) => {
  if (isLogin() && to.name === 'Login') {
    return '/'
  }
  if (!isLogin() && to.name !== 'Login') {
    return {
      name: 'Login',
    }
  }
  return true
})

可见新版全局路由钩子并不需要next方法来进行重定向,即使next方法仍可使用,可能在未来会被废弃,使用return 会更加适应新版本的变化,而且可以让代码更加清晰。

在全局路由钩子函数中动态添加路由

动态路由的添加

在实现后台权限动态渲染功能的时候,往往需要在全局路由钩子中动态添加路由。在vue-router V3版本中,使用router.addRoutes来批量添加路由,但是在V4版本中这个api被废弃了,需要使用router.addRoute方法,循环添加路由。添加了路由,也仅仅是注册了这个路由而已,如果需要当前进入的路由和新添加的路由匹配,还需要使用next({...to, replace: true}), 重定向到这个路由,才能渲染这个新路由对应的页面。

import { createRouter, createWebHistory } from 'vue-router'
import constantRoutes from './constant-routes';
import { getDynamicRoutes } from '@/api/index';

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      redirect: '/home'
    },
    ...constantRoutes // 静态路由包含了404页面,当所有页面都匹配不上时,会重定向到404
  ]
})

// 是否已经添加动态路由
let isAddRoutes = false

router.beforeEach(async (to, from, next) => {
  if (!isAddRoutes) {
    isAddRoutes = true
    const dynamicRoutes = await getDynamicRoutes()
    dynamicRoutes.forEach(route => {
      router.addRoute({
        path: route.path,
        name: route.name,
        component: route.component
      })
    })
    next({ ...to, replace: true })
  } else {
    next()
  }
})

export default router

解决第一次进入动态路由页面会渲染404页面的问题

使用以上方法动态添加路由,会导致第一次直接进入动态路由时,会渲染404页面,因为当第一次进入全局路由钩子时,动态路由还没加载完毕,匹配不到,优先匹配静态路由,就会直接渲染路由为*的404页面。

以前的做法,是在批量添加动态路由的最后,最后再动态添加404页面的路由,就可以给等待动态路由添加完毕,再渲染页面。

// 是否已经添加动态路由
let isAddRoutes = false

router.beforeEach(async (to, from, next) => {
  if (!isAddRoutes) {
    isAddRoutes = true
    const dynamicRoutes = await getDynamicRoutes()
    dynamicRoutes.forEach(route => {
      router.addRoute({
        path: route.path,
        name: route.name,
        component: route.component
      })
    })
    // 手动添加404页面
    router.addRoute({
      path: '/:pathMatch(.*)*',
      name: 'not-found',
      component: () => import('../views/NotFoundView.vue')
    })
    next({ ...to, replace: true })
  } else {
    next()
  }
})

这样,貌似就没有问题了,页面能够正常渲染,但是控制台会出现警告 image.png 这是因为,在等待路由加载的过程中,当前进入的路由是动态的,而我们的动态路由配置尚未加载完毕,未匹配中任何的路由,to.matched为空,所以vue-router就抛出了这个警告。

解决报警问题

根据上面的分析得出,是因为第一次进入动态路由的页面,动态路由正在加载中,当前路由未匹配到任何的已配置路由,所以会报警。

那么解决的办法,就是不在全局路由钩子动态添加404页面,而是写在静态路由配置中。当第一次从动态路由中进入,由于存在404页面,因为404的页面路由是*,就相当于匹配到了路由,不会出现报警. 虽然这时候匹配到了404页面,但是还没走到渲染404页面的逻辑,因为next还没执行,这时候再等待动态路由加载完毕再使用next重定向到进入的动态路由,即可避免报警的问题。

export default [
  {
    path: '/home',
    name: 'home',
    component: () => import('../views/HomeView.vue')
  },
  // 可以在静态路由中写死该配置
  {
    path: '/:pathMatch(.*)*',
    name: 'not-found',
    component: () => import('../views/NotFoundView.vue')
  }
]
// 是否已经添加动态路由
let isAddRoutes = false

router.beforeEach(async (to, from, next) => {
  if (!isAddRoutes) {
    isAddRoutes = true
    const dynamicRoutes = await getDynamicRoutes()
    dynamicRoutes.forEach(route => {
      router.addRoute({
        path: route.path,
        name: route.name,
        component: route.component
      })
    })
    // 返回to.fullPath,当添加完所有的动态路由后,触发重定向
    next(to.fullPath)
  } else {
    next()
  }
})

使用V4新版的写法,我们可以将next函数,换成return,代码会更加清晰

// 判断否已经添加动态路由,避免重复添加
let isAddRoutes = false

router.beforeEach(async (to, from) => {
  if (!isAddRoutes) {
    isAddRoutes = true
    const dynamicRoutes = await getDynamicRoutes()
    dynamicRoutes.forEach(route => {
      router.addRoute({
        path: route.path,
        name: route.name,
        component: route.component
      })
    })
    // 返回to.fullPath,当添加完所有的动态路由后,触发重定向
    return to.fullPath
  }
  return true
})

总结

新版的Vue-Router动态添加路由主要的两个坑点:

  1. 在静态路由中增加404页面,会导致从动态路由进入时会渲染404页面组件的内容
  2. 在动态添加路由的最后再添加404页面,会触发vue-router报警问题

解决方法:

  1. 404页面在静态路由中添加
  2. 添加动态路由后,直接重定向到当前进入路由的fullPath,使当前路由与动态路由匹配,渲染对应的页面

参考文章

[Vue Router warn]: No match found for location with path “xxxxx“

Vue-Router官方文档