Vue Router 使用教程:构建单页面应用的导航系统

50 阅读4分钟

Vue Router 是 Vue.js 官方的路由管理器,用于构建单页面应用(SPA)。它能够让你在不刷新页面的情况下切换视图,提供流畅的用户体验。本教程将带你从基础到进阶全面掌握 Vue Router。

目录

  1. 安装与配置
  2. 基础概念
  3. 路由配置
  4. 导航方式
  5. 动态路由
  6. 嵌套路由
  7. 编程式导航
  8. 路由守卫
  9. 路由元信息
  10. 高级特性

安装与配置

1. 安装 Vue Router

bash

npm install vue-router@4

2. 基本配置

javascript

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'

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

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

export default router

3. 在 main.js 中引入

javascript

// 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')

基础概念

RouterView 和 RouterLink

vue

<!-- App.vue -->
<template>
  <div id="app">
    <nav>
      <router-link to="/">首页</router-link>
      <router-link to="/about">关于</router-link>
    </nav>
    <router-view />
  </div>
</template>
  • <router-view>:路由出口,匹配到的组件将渲染在这里
  • <router-link>:导航链接,默认渲染为 <a> 标签

路由配置

基本路由配置

javascript

const routes = [
  {
    path: '/',           // 路径
    name: 'home',        // 路由名称(可选)
    component: Home,     // 对应组件
    alias: '/home',      // 别名
    redirect: '/welcome' // 重定向
  }
]

命名路由

javascript

{
  path: '/user/:id',
  name: 'user',
  component: User
}

vue

<router-link :to="{ name: 'user', params: { id: 123 }}">
  用户页面
</router-link>

导航方式

声明式导航

vue

<template>
  <div>
    <!-- 字符串路径 -->
    <router-link to="/home">首页</router-link>
    
    <!-- 使用 v-slot 自定义 -->
    <router-link to="/about" v-slot="{ href, route, navigate, isActive }">
      <a :href="href" @click="navigate" :class="{ active: isActive }">
        {{ route.name }}
      </a>
    </router-link>
    
    <!-- 对象形式 -->
    <router-link :to="{ path: '/profile' }">个人资料</router-link>
    
    <!-- 命名路由 -->
    <router-link :to="{ name: 'user', params: { id: 1 } }">
      用户详情
    </router-link>
  </div>
</template>

编程式导航

vue

<template>
  <button @click="goToHome">去首页</button>
  <button @click="goBack">返回</button>
</template>

<script setup>
import { useRouter } from 'vue-router'

const router = useRouter()

const goToHome = () => {
  router.push('/home')
  // 或者
  // router.push({ name: 'home' })
}

const goBack = () => {
  router.go(-1) // 后退一步
  // router.back() // 等同于 router.go(-1)
}

const replaceNavigation = () => {
  router.replace('/login') // 替换当前记录
}
</script>

动态路由

带参数的路由

javascript

// router/index.js
{
  path: '/user/:id',
  name: 'user',
  component: User
}

{
  path: '/product/:category/:id',
  name: 'product',
  component: Product
}

在组件中访问路由参数

vue

<template>
  <div>
    <h2>用户ID: {{ userId }}</h2>
    <h2>当前路径: {{ route.path }}</h2>
  </div>
</template>

<script setup>
import { useRoute } from 'vue-router'
import { computed } from 'vue'

const route = useRoute()

// 访问参数
const userId = computed(() => route.params.id)

// 访问查询参数
const searchQuery = computed(() => route.query.q)
</script>

可选参数和通配符

javascript

{
  path: '/user/:id?', // 可选参数
  component: User
}

{
  path: '/:pathMatch(.*)*', // 通配符路由(404页面)
  name: 'NotFound',
  component: NotFound
}

嵌套路由

配置嵌套路由

javascript

const routes = [
  {
    path: '/user/:id',
    component: User,
    children: [
      {
        // 当 /user/:id/profile 匹配成功
        path: 'profile',
        component: UserProfile
      },
      {
        // 当 /user/:id/posts 匹配成功
        path: 'posts',
        component: UserPosts
      },
      {
        // 默认子路由
        path: '',
        component: UserDashboard
      }
    ]
  }
]

嵌套路由组件

vue

<!-- User.vue -->
<template>
  <div class="user">
    <h2>用户 {{ $route.params.id }}</h2>
    <div class="user-nav">
      <router-link to="profile">资料</router-link>
      <router-link to="posts">文章</router-link>
    </div>
    <router-view /> <!-- 子路由将在这里渲染 -->
  </div>
</template>

编程式导航

各种导航方法

vue

<script setup>
import { useRouter } from 'vue-router'

const router = useRouter()

// 基本导航
const navigate = () => {
  // 字符串路径
  router.push('/users/1')
  
  // 对象
  router.push({ path: '/users/1' })
  
  // 命名路由
  router.push({ name: 'user', params: { id: 1 } })
  
  // 带查询参数
  router.push({ path: '/search', query: { q: 'vue' } })
}

// 替换当前位置
const replace = () => {
  router.replace('/login')
  // 或者
  router.push({ path: '/login', replace: true })
}

// 前进后退
const navigation = () => {
  router.go(1)  // 前进一步
  router.go(-1) // 后退一步
  router.back() // 后退
  router.forward() // 前进
}
</script>

导航结果处理

javascript

router.push('/dashboard').then(() => {
  // 导航成功
}).catch((error) => {
  if (error.name === 'NavigationDuplicated') {
    // 重复导航处理
    console.log('重复导航到相同位置')
  }
})

路由守卫

全局守卫

javascript

// router/index.js

// 全局前置守卫
router.beforeEach((to, from) => {
  // 返回 false 取消导航
  if (to.meta.requiresAuth && !isAuthenticated()) {
    return '/login'
  }
})

// 全局解析守卫
router.beforeResolve((to, from) => {
  // 在导航被确认前,组件被解析后调用
})

// 全局后置钩子
router.afterEach((to, from) => {
  // 导航完成后调用,无法改变导航
  document.title = to.meta.title || '默认标题'
})

路由独享守卫

javascript

{
  path: '/admin',
  component: Admin,
  beforeEnter: (to, from) => {
    // 仅在此路由进入时调用
    if (!isAdmin()) {
      return '/login'
    }
  }
}

组件内守卫

vue

<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'

// 在组件卸载前调用
onBeforeRouteLeave((to, from) => {
  const answer = window.confirm('确定要离开吗?未保存的更改将会丢失!')
  if (!answer) return false
})

// 在当前路由改变但组件复用时调用
onBeforeRouteUpdate(async (to, from) => {
  // 获取新的数据
  userData.value = await fetchUser(to.params.id)
})
</script>

路由元信息

定义和使用元信息

javascript

const routes = [
  {
    path: '/admin',
    component: Admin,
    meta: {
      requiresAuth: true,
      requiresAdmin: true,
      title: '管理后台'
    }
  },
  {
    path: '/profile',
    component: Profile,
    meta: {
      requiresAuth: true,
      title: '个人资料'
    }
  }
]

在守卫中使用元信息

javascript

router.beforeEach((to, from) => {
  // 检查元信息
  if (to.meta.requiresAuth && !isLoggedIn()) {
    return {
      path: '/login',
      query: { redirect: to.fullPath }
    }
  }
  
  if (to.meta.requiresAdmin && !isAdmin()) {
    return '/unauthorized'
  }
})

高级特性

路由懒加载

javascript

const routes = [
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: () => import('../views/Dashboard.vue')
  },
  {
    path: '/settings',
    name: 'Settings',
    component: () => import(/* webpackChunkName: "settings" */ '../views/Settings.vue')
  }
]

滚动行为

javascript

const router = createRouter({
  history: createWebHistory(),
  routes,
  scrollBehavior(to, from, savedPosition) {
    // 返回位置
    if (savedPosition) {
      return savedPosition
    } else if (to.hash) {
      return {
        el: to.hash,
        behavior: 'smooth'
      }
    } else {
      return { top: 0 }
    }
  }
})

路由过渡动画

vue

<template>
  <router-view v-slot="{ Component }">
    <transition name="fade" mode="out-in">
      <component :is="Component" />
    </transition>
  </router-view>
</template>

<style>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
</style>

完整示例

路由配置示例

javascript

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('../views/Home.vue'),
    meta: { title: '首页' }
  },
  {
    path: '/login',
    name: 'Login',
    component: () => import('../views/Login.vue'),
    meta: { title: '登录', guest: true }
  },
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: () => import('../views/Dashboard.vue'),
    meta: { requiresAuth: true, title: '仪表板' }
  },
  {
    path: '/user/:id',
    name: 'User',
    component: () => import('../views/User.vue'),
    props: true,
    meta: { requiresAuth: true },
    children: [
      {
        path: 'profile',
        name: 'UserProfile',
        component: () => import('../views/UserProfile.vue')
      },
      {
        path: 'settings',
        name: 'UserSettings',
        component: () => import('../views/UserSettings.vue')
      }
    ]
  },
  {
    path: '/:pathMatch(.*)*',
    name: 'NotFound',
    component: () => import('../views/NotFound.vue'),
    meta: { title: '页面未找到' }
  }
]

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

// 全局前置守卫
router.beforeEach((to, from) => {
  const isAuthenticated = checkAuth() // 你的认证逻辑
  
  if (to.meta.requiresAuth && !isAuthenticated) {
    return {
      path: '/login',
      query: { redirect: to.fullPath }
    }
  }
  
  if (to.meta.guest && isAuthenticated) {
    return '/dashboard'
  }
})

// 全局后置守卫
router.afterEach((to) => {
  document.title = to.meta.title || '我的应用'
})

export default router

总结

Vue Router 是 Vue.js 生态中非常重要的组成部分,它提供了:

  • 声明式路由配置:直观的路由映射关系
  • 编程式导航:灵活的导航控制
  • 路由守卫:完善的权限控制和导航拦截
  • 嵌套路由:复杂页面结构的组织
  • 懒加载:优化应用性能
  • 过渡效果:提升用户体验