第六章:Vue3 路由(Vue Router 4 从入门到实战)

一、入门:路由核心概念与环境搭建

  1. 为什么需要路由?在单页应用(SPA)中,页面内容切换无需刷新浏览器,而是通过“路由”实现URL与组件的映射——用户访问不同URL,路由匹配对应的组件并渲染,这是Vue项目实现多页面交互的核心。
  2. Vue Router 4 核心概念:
    • 路由规则(Routes):数组形式,每个规则包含path(URL路径)、component(对应组件)、name(路由名称,可选)等属性,定义“URL→组件”的映射关系。
    • 路由实例(Router Instance):用createRouter创建,传入路由规则和模式(historyhash),挂载到Vue应用后生效。
    • 路由视图(Router View):用<router-view>组件占位,路由匹配的组件会渲染到这个位置,是页面内容的“容器”。
    • 路由链接(Router Link):用<router-link>组件替代<a>标签实现页面跳转,避免刷新浏览器,通过to属性指定目标URL。
  3. 环境搭建步骤(详细版):
    • 第一步:安装依赖。在Vue3项目中执行命令:npm install vue-router@4(需指定版本4,避免安装旧版Vue Router 3)。
    • 第二步:创建路由配置文件。在src目录下新建router文件夹,创建index.js作为路由核心配置文件,初始代码如下:
    // src/router/index.js
    // 1. 引入路由核心函数和组件
    import { createRouter, createWebHistory } from 'vue-router'
    // 引入需要匹配的组件(可提前创建Home和About组件)
    import Home from '@/views/Home.vue'
    import About from '@/views/About.vue'
    
    // 2. 定义路由规则数组
    const routes = [
      {
        path: '/', // URL路径(根路径)
        name: 'Home', // 路由名称(可选,用于命名路由跳转)
        component: Home // 对应渲染的组件
      },
      {
        path: '/about',
        name: 'About',
        component: About
      }
    ]
    
    // 3. 创建路由实例
    const router = createRouter({
      // 路由模式:createWebHistory() 对应 HTML5 History 模式(URL无#),createWebHashHistory() 对应 Hash 模式(URL含#)
      history: createWebHistory(import.meta.env.BASE_URL), // import.meta.env.BASE_URL 是项目基础路径,在vite.config.js中配置
      routes // 传入路由规则
    })
    
    // 4. 导出路由实例,供main.js挂载
    export default router
    
    • 第三步:全局挂载路由。在src/main.js中引入路由实例,通过app.use(router)挂载:
    // src/main.js
    import { createApp } from 'vue'
    import App from './App.vue'
    import router from './router' // 引入路由实例
    
    const app = createApp(App)
    app.use(router) // 挂载路由
    app.mount('#app')
    
    • 第四步:添加路由视图和路由链接。在src/App.vue中,用<router-link>实现跳转,用<router-view>渲染组件:
    <!-- src/App.vue -->
    <template>
      <div class="app">
        <!-- 路由链接:to属性指定目标路径,active-class指定激活时的样式(可选) -->
        <nav>
          <router-link to="/" active-class="active">首页</router-link>
          <router-link to="/about" active-class="active">关于我们</router-link>
        </nav>
        <!-- 路由视图:匹配的组件会渲染到这里 -->
        <router-view class="content" />
      </div>
    </template>
    
    <style>
    .active { color: #42b983; font-weight: bold; } /* 激活路由的样式 */
    .content { margin-top: 20px; }
    </style>
    
    • 第五步:验证效果。执行npm run dev启动项目,点击“首页”“关于我们”,URL会切换(无#,History模式),页面内容实时切换且不刷新,说明路由配置成功。

二、精通:路由进阶功能与场景适配

1. 动态路由与路由传参(核心高频场景)

动态路由用于匹配“路径规则相同但参数不同”的URL,比如“商品详情页”——所有商品的详情页路径都是/goods/商品ID,通过动态参数id区分不同商品。

  • 第一步:定义动态路由规则。在routes数组中,用:参数名表示动态参数:
// src/router/index.js
import GoodsDetail from '@/views/GoodsDetail.vue' // 引入商品详情组件
const routes = [
  // ...其他路由
  {
    path: '/goods/:id', // :id 是动态参数,匹配 /goods/1、/goods/100 等路径
    name: 'GoodsDetail', // 命名路由,方便传参
    component: GoodsDetail,
    // 可选:明确指定路由参数的类型(避免类型错误,支持TypeScript)
    props: route => ({ id: Number(route.params.id) }) // 将params.id转为Number类型,通过props传给组件
  }
]
  • 第二步:路由传参的3种方式(场景对比):
    • 方式1:动态路由参数(params传参)。适合传递“必需参数”(如商品ID),参数会显示在URL中:
      • 跳转方式1(<router-link>): <router-link :to="'/goods/' + goodsId">查看详情</router-link>(goodsId是组件中的变量,如1)。
      • 跳转方式2(编程式导航,适合点击按钮后执行逻辑再跳转):
      import { useRouter } from 'vue-router' // 引入路由钩子
      const router = useRouter() // 创建路由实例
      const goToDetail = (goodsId) => {
        // 方式2.1:通过path跳转,params需在path中拼接
        router.push(`/goods/${goodsId}`)
        // 方式2.2:通过name跳转,params作为单独参数(推荐,更清晰)
        // router.push({ name: 'GoodsDetail', params: { id: goodsId } })
      }
      
      • 组件中接收参数:通过useRoute钩子获取路由信息,或通过props接收(需在路由规则中配置props):
      // 方式1:用useRoute获取
      import { useRoute } from 'vue-router'
      const route = useRoute()
      const goodsId = route.params.id // 注意:params参数默认是字符串类型,需手动转数字
      
      // 方式2:通过props接收(推荐,更符合组件化思想)
      // GoodsDetail.vue 中
      const props = defineProps({
        id: {
          type: Number,
          required: true // 必传参数
        }
      })
      console.log(props.id) // 直接使用,已转为Number类型
      
    • 方式2:查询参数(query传参)。适合传递“可选参数”(如分页、筛选条件),参数以?key=value形式显示在URL中(如/goods/1?tab=comment):
      • 编程式导航跳转:
      router.push({
        name: 'GoodsDetail', // 也可用path: '/goods/1'
        query: { tab: 'comment', page: 1 } // query参数,可传多个
      })
      
      • 组件中接收:const tab = route.query.tab(默认字符串类型,如'comment')。
    • 方式3:路由元信息(meta传参)。适合传递“路由配置相关的静态信息”(如页面标题、是否需要登录),参数不显示在URL中,仅在路由规则中定义:
      • 定义路由规则时添加meta
      const routes = [
        {
          path: '/about',
          name: 'About',
          component: About,
          meta: {
            title: '关于我们', // 页面标题
            requiresAuth: false // 是否需要登录(后续权限控制用)
          }
        },
        {
          path: '/user',
          name: 'User',
          component: () => import('@/views/User.vue'), // 懒加载(后续讲)
          meta: {
            title: '个人中心',
            requiresAuth: true // 需要登录才能访问
          }
        }
      ]
      
      • 组件中获取:const pageTitle = route.meta.title

2. 路由懒加载(优化性能核心手段)

默认情况下,Vue项目打包时会将所有路由组件合并到一个JS文件中,导致文件体积过大,首屏加载速度慢。路由懒加载通过“按需加载”——只有当用户访问该路由时,才加载对应的组件JS文件,减小首屏加载体积。

  • 实现方式:将路由规则中component的“静态引入”改为“动态import函数引入”,代码如下:
// src/router/index.js
// 静态引入(不推荐,会打包到主文件)
// import User from '@/views/User.vue'

// 动态引入(路由懒加载,推荐)
const User = () => import('@/views/User.vue')

const routes = [
  {
    path: '/user',
    name: 'User',
    component: User // 使用懒加载组件
    // 可选:给懒加载的组件分组(打包时合并到同一个JS文件)
    // component: () => import(/* webpackChunkName: "user" */ '@/views/User.vue')
  },
  // 更简洁的写法:直接在component中写动态import
  {
    path: '/order',
    name: 'Order',
    component: () => import('@/views/Order.vue')
  }
]
  • 效果验证:启动项目后,首次加载仅加载首页相关JS文件;访问/user时,浏览器会额外加载User.vue对应的JS文件(可在Chrome开发者工具“Network”面板查看)。

3. 嵌套路由(实现页面布局复用)

当多个页面有相同的“公共布局”(如左侧菜单、顶部导航),可通过嵌套路由实现——公共布局作为“父路由组件”,不同内容作为“子路由组件”,渲染到父组件的<router-view>中。

  • 示例:实现“用户中心”页面,包含“个人信息”“我的订单”两个子页面,共享顶部导航。
  • 第一步:创建父路由组件(UserLayout.vue),在组件中添加<router-view>用于渲染子组件:
<!-- src/views/UserLayout.vue(父路由组件) -->
<template>
  <div class="user-layout">
    <!-- 公共顶部导航 -->
    <div class="user-header">个人中心</div>
    <!-- 子路由组件渲染位置 -->
    <router-view class="user-content" />
  </div>
</template>

<style>
.user-header { padding: 16px; border-bottom: 1px solid #eee; }
.user-content { padding: 16px; }
</style>
  • 第二步:创建子路由组件(UserProfile.vueUserOrders.vue),分别实现“个人信息”和“我的订单”内容。
  • 第三步:配置嵌套路由规则,在父路由的children数组中定义子路由:
// src/router/index.js
const routes = [
  {
    path: '/user',
    name: 'User',
    component: () => import('@/views/UserLayout.vue'), // 父路由组件
    meta: { requiresAuth: true },
    // 子路由规则(children数组)
    children: [
      {
        // 子路由path不加/,表示相对父路由路径(即 /user/profile)
        path: 'profile',
        name: 'UserProfile',
        component: () => import('@/views/UserProfile.vue'),
        meta: { title: '个人信息' }
      },
      {
        path: 'orders',
        name: 'UserOrders',
        component: () => import('@/views/UserOrders.vue'),
        meta: { title: '我的订单' }
      },
      // 子路由默认跳转:当访问 /user 时,自动跳转到 /user/profile
      {
        path: '',
        redirect: 'profile'
      }
    ]
  }
]
  • 第四步:添加子路由链接。在父路由组件(UserLayout.vue)中添加<router-link>,实现子页面切换:
<!-- UserLayout.vue 中 -->
<template>
  <div class="user-layout">
    <div class="user-header">个人中心</div>
    <!-- 子路由链接 -->
    <div class="user-tabs">
      <router-link to="/user/profile" active-class="active">个人信息</router-link>
      <router-link to="/user/orders" active-class="active">我的订单</router-link>
    </div>
    <router-view class="user-content" />
  </div>
</template>

<style>
.user-tabs { margin: 16px 0; display: flex; gap: 20px; }
.user-tabs .active { color: #42b983; border-bottom: 2px solid #42b983; }
</style>
  • 效果:访问/user时,自动渲染UserLayoutUserProfile;点击“我的订单”,URL变为/user/ordersUserLayout保持不变,仅中间内容切换为UserOrders

4. 路由守卫(实现权限控制与页面拦截)

路由守卫是“路由跳转前后执行的钩子函数”,可用于权限控制(如未登录拦截到登录页)、页面标题修改、埋点统计等场景。Vue Router 4 提供3类守卫:全局守卫、路由独享守卫、组件内守卫。

  • 场景1:全局前置守卫(beforeEach)——未登录拦截。所有路由跳转前都会执行,适合全局权限控制:
// src/router/index.js
import { useUserStore } from '@/store/userStore' // 引入用户Store(第五章创建的)

// 全局前置守卫:跳转前执行
router.beforeEach((to, from, next) => {
  // to:即将跳转到的路由对象
  // from:当前要离开的路由对象
  // next:函数,控制是否允许跳转(Vue Router 4 中可直接返回true/false或路由对象,next已废弃但仍兼容)

  // 1. 修改页面标题(利用路由meta信息)
  document.title = to.meta.title || 'Vue3 路由示例'

  // 2. 权限控制:判断目标路由是否需要登录(requiresAuth为true)
  const userStore = useUserStore() // 创建用户Store实例
  const requiresAuth = to.meta.requiresAuth
  if (requiresAuth && !userStore.isLogin) {
    // 未登录且需要登录,拦截到登录页,并记录当前路由(登录后可跳回)
    return {
      name: 'Login', // 登录页路由名称(需提前创建Login组件和路由)
      query: { redirect: to.fullPath } // 记录当前要访问的路径,存到query参数
    }
  }

  // 允许跳转
  return true
})
  • 场景2:组件内守卫——离开页面时提示未保存的内容。在组件中定义,仅当前组件对应的路由生效:
<!-- UserProfile.vue(个人信息组件) -->
<script setup>
import { onBeforeRouteLeave } from 'vue-router'
import { ref } from 'vue'

const isEdited = ref(false) // 标记是否修改了内容未保存

// 组件内守卫:离开当前路由前执行
onBeforeRouteLeave((to, from, next) => {
  if (isEdited.value) {
    const confirmLeave = window.confirm('内容未保存,确定要离开吗?')
    if (confirmLeave) {
      next() // 确认离开,允许跳转
    } else {
      next(false) // 取消离开,阻止跳转
    }
  } else {
    next() // 无未保存内容,允许跳转
  }
})
</script>

三、实战:路由综合案例(用户登录与个人中心)

  1. 需求拆解:实现“登录页→首页→个人中心(含子页面)”的完整流程,包含:未登录访问个人中心拦截到登录页、登录后跳回原目标页面、个人中心子页面切换、页面标题动态修改。
  2. 前置准备:已创建以下组件和配置:
    • 组件:Login.vue(登录页)、Home.vue(首页)、UserLayout.vue(个人中心父组件)、UserProfile.vue(个人信息)、UserOrders.vue(我的订单)。
    • 路由:已配置上述组件的路由,且/user及子路由的meta.requiresAuth设为true
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐