Skip to content

Vue Router - 路由管理

Vue Router 是 Vue.js 的官方路由管理器。它和 Vue.js 深度集成,让构建单页面应用变得易如反掌。

什么是 Vue Router?

Vue Router 是 Vue.js 官方的路由管理器。它的功能包括:

  • 嵌套的路由/视图表
  • 模块化的、基于组件的路由配置
  • 路由参数、查询、通配符
  • 基于 Vue.js 过渡系统的视图过渡效果
  • 细粒度的导航控制
  • 带有自动激活的 CSS class 的链接
  • HTML5 历史模式或 hash 模式,在 IE9 中自动降级
  • 自定义的滚动条行为

安装

npm

bash
npm install vue-router@4

yarn

bash
yarn add vue-router@4

基本使用

1. 定义路由组件

vue
<!-- Home.vue -->
<template>
  <div>
    <h1>首页</h1>
    <p>欢迎来到首页!</p>
  </div>
</template>
vue
<!-- About.vue -->
<template>
  <div>
    <h1>关于我们</h1>
    <p>这是关于页面。</p>
  </div>
</template>

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. 创建和挂载根实例

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

4. 在模板中使用

vue
<!-- App.vue -->
<template>
  <div id="app">
    <nav>
      <router-link to="/">首页</router-link> |
      <router-link to="/about">关于</router-link>
    </nav>
    <router-view/>
  </div>
</template>

动态路由匹配

路径参数

javascript
const routes = [
  // 动态路径参数 以冒号开头
  { path: '/user/:id', component: User }
]
vue
<!-- User.vue -->
<template>
  <div>
    <h1>用户 {{ $route.params.id }}</h1>
  </div>
</template>

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

const route = useRoute()
console.log(route.params.id)
</script>

响应路由参数的变化

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

const route = useRoute()

watch(
  () => route.params.id,
  (newId, oldId) => {
    // 对路由变化做出响应...
    console.log(`从用户 ${oldId} 切换到用户 ${newId}`)
  }
)
</script>

嵌套路由

javascript
const routes = [
  {
    path: '/user/:id',
    component: User,
    children: [
      {
        // 当 /user/:id/profile 匹配成功,
        // UserProfile 会被渲染在 User 的 <router-view> 中
        path: 'profile',
        component: UserProfile
      },
      {
        // 当 /user/:id/posts 匹配成功
        // UserPosts 会被渲染在 User 的 <router-view> 中
        path: 'posts',
        component: UserPosts
      }
    ]
  }
]
vue
<!-- User.vue -->
<template>
  <div class="user">
    <h2>用户 {{ $route.params.id }}</h2>
    <router-view></router-view>
  </div>
</template>

编程式导航

router.push()

javascript
// 字符串
router.push('/home')

// 对象
router.push({ path: '/home' })

// 命名的路由
router.push({ name: 'user', params: { userId: '123' } })

// 带查询参数,变成 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })

在组合式 API 中使用

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

const router = useRouter()

function goToHome() {
  router.push('/')
}

function goBack() {
  router.go(-1)
}
</script>

命名路由

javascript
const routes = [
  {
    path: '/user/:userId',
    name: 'user',
    component: User
  }
]
vue
<template>
  <router-link :to="{ name: 'user', params: { userId: 123 }}">
    用户
  </router-link>
</template>

路由守卫

全局前置守卫

javascript
router.beforeEach((to, from, next) => {
  // 检查用户是否已登录
  if (to.meta.requiresAuth && !isLoggedIn()) {
    next('/login')
  } else {
    next()
  }
})

路由独享的守卫

javascript
const routes = [
  {
    path: '/admin',
    component: Admin,
    beforeEnter: (to, from, next) => {
      // 检查管理员权限
      if (isAdmin()) {
        next()
      } else {
        next('/unauthorized')
      }
    }
  }
]

组件内的守卫

vue
<script>
export default {
  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不能获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
    next()
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 可以访问组件实例 `this`
    next()
  },
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
    const answer = window.confirm('确定要离开吗?未保存的更改将会丢失。')
    if (answer) {
      next()
    } else {
      next(false)
    }
  }
}
</script>

组合式 API 中的守卫

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

// 与 beforeRouteLeave 相同,无法访问 `this`
onBeforeRouteLeave((to, from) => {
  const answer = window.confirm('确定要离开吗?')
  // 取消导航并停留在同一页面上
  if (!answer) return false
})

// 与 beforeRouteUpdate 相同
onBeforeRouteUpdate(async (to, from) => {
  // 仅当 id 更改时才获取用户信息
  if (to.params.id !== from.params.id) {
    userData.value = await fetchUser(to.params.id)
  }
})
</script>

路由元信息

javascript
const routes = [
  {
    path: '/admin',
    component: Admin,
    meta: { requiresAuth: true, role: 'admin' }
  },
  {
    path: '/profile',
    component: Profile,
    meta: { requiresAuth: true }
  }
]
javascript
router.beforeEach((to, from, next) => {
  if (to.meta.requiresAuth) {
    // 检查认证状态
    if (!isAuthenticated()) {
      next('/login')
      return
    }
    
    // 检查角色权限
    if (to.meta.role && !hasRole(to.meta.role)) {
      next('/unauthorized')
      return
    }
  }
  
  next()
})

懒加载路由

javascript
const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // 路由级别的代码分割
    // 这会为这个路由生成一个单独的 chunk (about.[hash].js)
    // 当访问这个路由时才会懒加载
    component: () => import('@/views/About.vue')
  }
]

过渡效果

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
const router = createRouter({
  history: createWebHistory(),
  routes,
  scrollBehavior(to, from, savedPosition) {
    // 如果有保存的位置,就滚动到那里
    if (savedPosition) {
      return savedPosition
    }
    // 如果有锚点,就滚动到锚点
    if (to.hash) {
      return {
        el: to.hash,
        behavior: 'smooth'
      }
    }
    // 否则滚动到顶部
    return { top: 0 }
  }
})

最佳实践

1. 路由结构组织

javascript
// 按功能模块组织路由
const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('@/views/Home.vue')
  },
  {
    path: '/auth',
    component: () => import('@/layouts/AuthLayout.vue'),
    children: [
      {
        path: 'login',
        name: 'Login',
        component: () => import('@/views/auth/Login.vue')
      },
      {
        path: 'register',
        name: 'Register',
        component: () => import('@/views/auth/Register.vue')
      }
    ]
  },
  {
    path: '/dashboard',
    component: () => import('@/layouts/DashboardLayout.vue'),
    meta: { requiresAuth: true },
    children: [
      {
        path: '',
        name: 'Dashboard',
        component: () => import('@/views/dashboard/Overview.vue')
      },
      {
        path: 'profile',
        name: 'Profile',
        component: () => import('@/views/dashboard/Profile.vue')
      }
    ]
  }
]

2. 路由守卫的使用

javascript
// 创建一个权限检查的工具函数
function createAuthGuard() {
  return (to, from, next) => {
    const token = localStorage.getItem('token')
    
    if (to.meta.requiresAuth && !token) {
      next('/auth/login')
    } else if (to.meta.guest && token) {
      next('/dashboard')
    } else {
      next()
    }
  }
}

router.beforeEach(createAuthGuard())

3. 错误处理

javascript
// 404 页面
const routes = [
  // ... 其他路由
  {
    path: '/:pathMatch(.*)*',
    name: 'NotFound',
    component: () => import('@/views/NotFound.vue')
  }
]

下一步

现在你已经了解了 Vue Router 的基本使用,继续学习:

Vue Router 是构建单页面应用的重要工具,掌握它将让你的 Vue.js 开发更加高效!

基于 Vue.js 官方文档构建的学习宝典