官方文档:

vue2 对应的路由: vue-router@3 (opens new window)

vue3 对应的路由: vue-router@4 (opens new window)

# 一、基础变化

# 1、路由引入

  由于 vue3 采用的是 composition api,所以引入 vue-router 有些许不同:

  • vue-router@4
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'

const routes = []

export default createRouter({
  history: createWebHashHistory() / createWebHistory(),
  routes
})
  • vue-router@3
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)

const routes = []

export default new VueRouterr({
  mode: 'hash' / 'history'
  routes
})

# 2、组件中使用

  因为 setup 中不能访 this,所以提供两个 api 来获取 router 和 route , useRouter() 和 useRoute()

<script setup lang="ts">
import { useRouter } from 'vue-router'

const router = useRouter()

// router.push()
</script>

  而 vue2 中直接使用 this.$router.push()即可。

<script>
export default {
  mounted() {
    // this.$router.push()
  }
}
</script>

# 3、路由守卫

  在 vue-router@3 中,官方强调了要确保 next 函数在任何给定的导航守卫中都被严格调用一次,但实际开发中,还是容易发生意外:

错误示例
  • 错误
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  // 如果用户未能验证身份,则 `next` 会被调用两次
  next()
})
  • 正确
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  else next()
})

 在 vue-router@4 中,官方采用直接 return 的方式解决该问题:

vue-router@3 中
  • 方式 1
// 设置全局前置守卫
router.beforeEach((to, from, next) => {
  const token = localStorage.getItem('token')

  if (to.path.startWith('/main') && !token) {
    next('login')
  }
})
export default router
  • 方式 2
// 白名单页面
const whiteList = ['/login', '/reg']

// 使用 router.beforeEach 注册一个全局前置守卫,判断用户是否登陆
router.beforeEach((to, from, next) => {
  if (whiteList.includes(to.path)) {
    next()
  } else {
    const token = localStorage.getItem('token')

    if (token === null || token === '') {
      next('/login')
    } else {
      next()
    }
  }
})
  • 方式 1

如果后台页面路径都以 /main 开头的换,导航守卫就简单的多了:

// 设置全局前置守卫
router.beforeEach((to, from) => {
  const token = localStorage.getItem('token')

  if (to.path.startsWith('/main') && !token) {
    return '/login'
  }
})
export default router
  • 方式 2

但也有后台页面路径不规则的情况

// 白名单页面
const whiteList = ['/login', '/reg']

// 使用 router.beforeEach 注册一个全局前置守卫,判断用户是否登陆
router.beforeEach((to, from) => {
  if (whiteList.includes(to.path)) {
    return to.path // 不返回或返回undefined也可以
  } else {
    const token = localStorage.getItem('token')

    if (token === null || token === '') {
      return '/login' // 也可以返回一个详细的路由对象
    } else {
      return to.path // 也可以不返回或返回undefined
    }
  }
})

# 4、Api 变化

  在 Vue 3 中,router 和 route 对象只能在组件的 setup 函数、全局上下文中中使用。

组件的 setup 函数中
  • 选项式中
import { ref, onMounted } from 'vue'

export default {
  setup(props, context) {
    const router = context.root.$router
    const route = context.root.$route

    // 在这里可以使用 router 和 route 对象进行操作

    onMounted(() => {
      console.log('当前路由路径:', route.path)
    })

    return {
      // 返回给模板使用的数据和方法
    }
  }
}
  • 组合式中
import { useRouter, useRoute } from 'vue-router'

const router = useRouter()
const route = useRoute()

// 在这里可以使用 router 和 route 对象进行操作
console.log('当前路由路径:', route.path)

return {
  // 返回给模板使用的数据和方法
}
全局上下文中

  一旦注入完成,你就可以在任何组件或其他地方直接访问 $router$route,而无需在每个组件的 setup 函数中重新获取。

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

const app = createApp(App)

// 将 $router 和 $route 注入全局上下文
app.config.globalProperties.$router = router
app.config.globalProperties.$route = router.currentRoute

app.use(router)
app.mount('#app')
<template>
  <div>
    <p>当前路由路径: {{ $route.path }}</p>
    <router-link to="/other">跳转到其他页面</router-link>
  </div>
</template>

<script>
export default {
  mounted() {
    // 在组件的生命周期钩子中可以直接访问全局上下文中的 $router 和 $route
    console.log('全局上下文中的 $router:', this.$router)
    console.log('全局上下文中的 $route:', this.$route)
  }
}
</script>
// vue2
this.$route
this.$router

// vue3
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()

// 示例:
// route.path
// router.currentRoute.value.path (不建议)

// router.push('/login')

# 二、动态权限路由

根据不同的角色,生成不同的菜单

  • 方案一:对应的菜单的路由还是注册的,只不过是更加条件显示
  • 方案二:根据不同的角色动态的进行路由注册

# 1、添加 addRoute (opens new window)

addRoute(route): () => void

# 这里强调需要name👀属性
addRoute(parentName, route): () => void
const router = createRouter({
  routes: [
    {
      name: 'partOne',
      path: '/part1',
      component: () => import('../page/PartOne.vue'),

      // 二级路由
      children: [
        {
          // 注意二级路由是✨不用加'/'的
          name: 'one'
          path: 'one',
          component: () => import('../page/One.vue')
        },
        {
          name: 'two'
          path: 'two',
          component: () => import('../page/Two.vue')
        }
      ]
    }
  ]
})

let isAdmin = true // 从服务器中获取角色信息

if (isAdmin) {
  // 添加一级路由:addRoute(route): () => void
  router.addRoute({
    path: '/admin',
    component: () => import('../page/Admin.vue')
  })

  // 添加二级路由:addRoute(parentName, route): () => void
  router.addRoute('partOne', {
    path: 'two',
    component: () => import('../page/Two.vue')
  })
}

# 2、删除 removeRoute (opens new window)

提示

  利用路由中 name 属性值的唯一性(覆盖、删除)

  当路由被删除时,所有的别名和子路由也会被同时删除

vue-router@3 中路由的 ✍ 清除

  vue-element-admin 中动态清除已注册的路由方案

  在 github 的 vue-router 的官方 issues 中有人提出了解决方案 (opens new window)

利用的是源码的实现:vue-router 会将 routes 选项中的路由数据传送给 createMatcher()方法,该方法会最终返回两个函数:addRoutes()、match()等,这些方法和对象最终会被保存到 router.matcher (opens new window)

const createRouter = () =>
  new Router({
    routes: constantRoutes
  })

const router = createRouter()

export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher
}

export default router

  但是上面的这种方案也有一定的问题 (opens new window)

所以项目中的含有通配符的路由()不应该放在 constantRoutes 内!!!

  router.addRoutes([...])

  // or

  router.options.routes = [...]
// 1、覆盖
router.addRoute('partOne', {
  name: 'two' // 覆盖
  path: 'two-pro',
  component: 'TwoPro'
})

// 2、指定名称
router.removeRoute('two') // 删除

// 3、回调
const removeRoute = router.addRoute(routeRecord)
removeRoute() // 删除路由如果存在的话

# 3、其他

Router 接口 (opens new window)

  • router.hasRoute()
  • router.getRoutes()
更新于 : 8/7/2024, 2:16:31 PM