Skip to content

Vue Router高级用法与复杂路由场景实现

Vue Router是Vue生态中处理路由的核心工具,除了基础的页面跳转,它还提供了丰富的高级特性,支持复杂应用的路由设计。本文将深入解析Vue Router的高级用法,包括动态路由、嵌套路由、路由守卫等,并结合实际场景说明如何落地实现。

一、动态路由与路由参数:处理变化的路径

动态路由用于匹配路径结构相同但参数不同的路由(如详情页),通过:语法定义参数,使路由规则具备灵活性。

1. 动态路由配置

javascript
// router/index.js(Vue3)
import {createRouter, createWebHistory} from 'vue-router'
import UserProfile from '../views/UserProfile.vue'

const routes = [
    // 动态参数:userId,匹配/user/1、/user/2等路径
    {path: '/user/:userId', component: UserProfile}
]

const router = createRouter({
    history: createWebHistory(),
    routes
})

参数规则扩展

  • 多参数:/post/:postId/comment/:commentId(匹配/post/1/comment/5
  • 可选参数:/search/:query?query可省略,匹配/search/search/vue
  • 正则约束:/user/:userId(\\d+)(仅匹配数字ID,如/user/123,不匹配/user/abc

2. 路由参数的获取与响应

(1)获取参数

  • 选项式API:通过this.$route.params获取

    vue
    <script>
    export default {
      mounted() {
        console.log('用户ID:', this.$route.params.userId)
      }
    }
    </script>
  • 组合式API:通过useRoute钩子获取

    vue
    <script setup>
    import { useRoute } from 'vue-router'
    const route = useRoute()
    console.log('用户ID:', route.params.userId)
    </script>

(2)响应参数变化

当路由参数变化(如从/user/1跳转到/user/2)时,组件不会重新创建,需手动监听参数变化:

vue
<!-- 选项式API:监听$route变化 -->
<script>
  export default {
    watch: {
      $route(to, from) {
        // 当userId变化时重新加载数据
        this.loadUser(to.params.userId)
      }
    }
  }
</script>

<!-- 组合式API:监听route.params -->
<script setup>
  import {useRoute, watch} from 'vue-router'

  const route = useRoute()

  watch(
      () => route.params.userId,
      (newId) => {
        loadUser(newId) // 重新加载数据
      }
  )
</script>

更优雅的方式:使用组件内路由守卫beforeRouteUpdate

vue

<script>
  export default {
    beforeRouteUpdate(to, from) {
      // 参数变化时触发,可直接修改组件状态
      this.userId = to.params.userId
      this.loadUser(this.userId)
    }
  }
</script>

二、嵌套路由:构建层级化页面结构

嵌套路由用于实现“页面内嵌套子页面”的场景(如后台管理系统的侧边栏+主内容区),通过children配置子路由,配合<router-view> 嵌套渲染。

1. 嵌套路由配置

javascript
// router/index.js
const routes = [
    {
        path: '/dashboard',
        component: DashboardLayout, // 父组件(包含公共布局)
        children: [
            // 子路由:path为空表示默认子路由
            {path: '', component: DashboardHome},
            // 子路由:/dashboard/settings
            {path: 'settings', component: DashboardSettings},
            // 子路由:/dashboard/profile
            {path: 'profile', component: DashboardProfile}
        ]
    }
]

2. 组件中的嵌套渲染

父组件(DashboardLayout.vue)需包含<router-view>用于渲染子路由:

vue
<!-- DashboardLayout.vue -->
<template>
  <div class="dashboard">
    <!-- 公共侧边栏 -->
    <aside>
      <nav>
        <router-link to="/dashboard">首页</router-link>
        <router-link to="/dashboard/settings">设置</router-link>
      </nav>
    </aside>

    <!-- 子路由内容将渲染到这里 -->
    <main>
      <router-view></router-view>
    </main>
  </div>
</template>

注意事项

  • 子路由的path如果以/开头,则表示根路径,不受父路由path影响;
  • 嵌套层级不宜过深(建议不超过3层),否则会增加维护成本。

三、路由守卫:控制路由访问与跳转

路由守卫用于在路由跳转过程中添加“拦截逻辑”,如登录验证、权限检查、未保存提示等。Vue Router提供三类守卫,覆盖路由生命周期的不同阶段。

1. 全局守卫:影响所有路由

全局守卫注册在路由实例上,对所有路由生效,常用场景:登录验证、全局日志记录。

(1)beforeEach:路由跳转前触发

javascript
// router/index.js
router.beforeEach((to, from, next) => {
    // to:目标路由对象
    // from:当前导航正要离开的路由
    // next:函数,决定是否继续跳转(必须调用)

    // 示例:未登录用户访问需要登录的页面时,重定向到登录页
    const isLogin = localStorage.getItem('token')
    const requiresAuth = to.meta.requiresAuth // 结合元信息判断是否需要登录

    if (requiresAuth && !isLogin) {
        next('/login') // 重定向到登录页
    } else {
        next() // 允许跳转
    }
})

(2)beforeResolve:导航确认前触发(所有组件内守卫和异步路由组件解析后)

javascript
router.beforeResolve((to, from, next) => {
    // 常用于导航确认前的最后检查(如数据预加载)
    next()
})

(3)afterEach:路由跳转后触发(无next

javascript
router.afterEach((to, from) => {
    // 常用于页面埋点、修改页面标题
    document.title = to.meta.title || '默认标题'
})

2. 路由独享守卫:仅影响当前路由

在路由配置中通过beforeEnter定义,仅对当前路由生效,适合单独路由的权限控制。

javascript
const routes = [
    {
        path: '/admin',
        component: AdminPanel,
        // 路由独享守卫
        beforeEnter: (to, from, next) => {
            // 示例:仅管理员可访问
            const userRole = localStorage.getItem('role')
            if (userRole === 'admin') {
                next()
            } else {
                next('/forbidden') // 无权限,跳转到禁止页
            }
        }
    }
]

3. 组件内守卫:控制组件的路由行为

在组件内部定义,用于组件相关的路由逻辑(如未保存表单的离开确认)。

vue

<script>
  export default {
    // 进入组件前触发(此时组件实例未创建,不能用this)
    beforeRouteEnter(to, from, next) {
      // 可通过next的回调访问组件实例
      next(vm => {
        vm.fetchData(to.params.id) // 调用组件方法
      })
    },

    // 组件复用(参数变化)时触发(如从/post/1到/post/2)
    beforeRouteUpdate(to, from, next) {
      this.id = to.params.id
      this.fetchData(this.id)
      next()
    },

    // 离开组件时触发
    beforeRouteLeave(to, from, next) {
      // 示例:未保存表单时提示确认
      if (this.formChanged) {
        if (confirm('表单未保存,确定离开吗?')) {
          next() // 确认离开
        } else {
          next(false) // 取消离开
        }
      } else {
        next()
      }
    }
  }
</script>

四、路由懒加载与代码分割:优化首屏加载

路由懒加载通过“按需加载”路由组件,减少首屏JS体积,提升加载速度。其原理是将组件打包为单独的chunk,仅在访问对应路由时才加载。

1. 基础实现(动态import)

javascript
// 非懒加载(初始加载时全部加载)
import Home from '../views/Home.vue'

// 懒加载(访问时才加载)
const About = () => import('../views/About.vue')

const routes = [
    {path: '/', component: Home},
    {path: '/about', component: About}
]

2. 命名chunk(方便调试)

通过/* webpackChunkName: "chunk-name" */注释指定chunk名称(Webpack/Vite支持):

javascript
// 命名为"user"的chunk
const UserProfile = () => import(/* webpackChunkName: "user" */ '../views/UserProfile.vue')
const UserSettings = () => import(/* webpackChunkName: "user" */ '../views/UserSettings.vue')

打包后,这两个组件会被合并到user.js中,减少HTTP请求。

3. 配合Suspense(Vue3)

Vue3的Suspense可优雅处理懒加载组件的加载状态:

vue

<template>
  <Suspense>
    <!-- 加载成功时显示的内容 -->
    <template #default>
      <router-view></router-view>
    </template>

    <!-- 加载中显示的占位内容 -->
    <template #fallback>
      <div>加载中...</div>
    </template>
  </Suspense>
</template>

五、路由元信息与滚动行为:精细化控制

1. 路由元信息(meta)

meta用于存储路由的附加信息(如页面标题、是否需要登录),可在路由守卫中访问,实现灵活的逻辑控制。

javascript
// 路由配置
const routes = [
    {
        path: '/',
        component: Home,
        meta: {
            title: '首页', // 页面标题
            requiresAuth: false, // 不需要登录
            keepAlive: true // 需要缓存组件
        }
    },
    {
        path: '/profile',
        component: Profile,
        meta: {
            title: '个人中心',
            requiresAuth: true, // 需要登录
            roles: ['user', 'admin'] // 允许访问的角色
        }
    }
]

// 在全局守卫中使用meta
router.beforeEach((to, from, next) => {
    // 设置页面标题
    document.title = to.meta.title || '应用名称'

    // 权限检查
    if (to.meta.requiresAuth) {
        // 检查登录状态...
    }
    next()
})

2. 滚动行为(scrollBehavior)

控制路由跳转时页面的滚动位置,提升用户体验(如返回顶部、保持滚动位置)。

javascript
const router = createRouter({
    history: createWebHistory(),
    routes,
    // 滚动行为配置
    scrollBehavior(to, from, savedPosition) {
        // 场景1:按下浏览器后退/前进按钮时,恢复之前的滚动位置
        if (savedPosition) {
            return savedPosition
        }

        // 场景2:跳转到新路由时,默认滚动到顶部
        return {top: 0}

        // 场景3:滚动到指定元素(通过选择器)
        if (to.hash) {
            return {
                el: to.hash, // 滚动到#anchor元素
                behavior: 'smooth' // 平滑滚动
            }
        }
    }
})

六、复杂场景实战:动态路由与权限控制

在后台管理系统中,常需要根据用户角色动态生成路由。结合addRoute方法和权限判断,可实现这一需求。

1. 动态添加路由(addRoute

javascript
// 路由权限逻辑(store中)
export const usePermissionStore = defineStore('permission', {
    actions: {
        generateRoutes(roles) {
            // 1. 定义所有需要权限的路由
            const asyncRoutes = [
                {path: '/admin', component: Admin, meta: {roles: ['admin']}},
                {path: '/editor', component: Editor, meta: {roles: ['admin', 'editor']}}
            ]

            // 2. 根据用户角色筛选路由
            const accessibleRoutes = asyncRoutes.filter(route => {
                return route.meta.roles.some(role => roles.includes(role))
            })

            // 3. 动态添加路由
            accessibleRoutes.forEach(route => {
                router.addRoute(route) // 添加到根路由
                // 若需添加到嵌套路由:router.addRoute('父路由name', route)
            })

            return accessibleRoutes
        }
    }
})

2. 实现步骤

  1. 用户登录后获取角色信息;
  2. 调用generateRoutes根据角色筛选并添加路由;
  3. 路由添加完成后跳转到首页或之前的目标页。
vue
<!-- 登录组件 -->
<script setup>
  import {useRouter} from 'vue-router'
  import {usePermissionStore} from '../store/permission'

  const router = useRouter()
  const permissionStore = usePermissionStore()

  const handleLogin = async () => {
    // 1. 登录获取用户信息(含角色)
    const {roles} = await api.login(/* 账号密码 */)

    // 2. 动态生成路由
    const accessibleRoutes = await permissionStore.generateRoutes(roles)

    // 3. 跳转到首页或之前的目标页(如登录前想访问的页面)
    router.push(router.history.pending.fullPath || '/')
  }
</script>

总结

Vue Router的高级特性为复杂路由场景提供了完整解决方案:

  • 动态路由处理参数化路径,适合详情页等场景;
  • 嵌套路由构建层级页面,如后台管理系统的布局;
  • 路由守卫控制访问权限,实现登录验证、权限检查;
  • 懒加载优化首屏性能,减少初始加载时间;
  • 元信息与滚动行为提升用户体验,实现精细化控制。

在实际开发中,需根据业务场景合理组合这些特性,同时注意路由设计的简洁性(避免过度嵌套)和可维护性(路由模块化)。掌握这些高级用法,能让你轻松应对从简单应用到大型复杂系统的路由需求。