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 开发更加高效!