Vue Router 是 Vue 官方为“单页应用”量身定制的导航管家。它把 URL 当成地图坐标,把组件当成房间,只需在地址栏输入路径,就能瞬间“瞬移”到对应房间,无需整页刷新。最新 v4 版本全面拥抱 Vue3:API 组合式、TypeScript 类型完备;路由懒加载粒度更细,首屏加载速度肉眼可见地提升;嵌套、命名视图、动态增删路由一气呵成。一句话:写更少,跑更快,体验更丝滑。

核心概念
- 路由 (Route): URL 路径与组件的映射关系
- 路由器 (Router): 管理所有路由的实例
- 导航 (Navigation): 从一个路由切换到另一个路由的过程
安装
npm install vue-router@4基础配置
// src/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
})
exportdefault 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')1. 基础路由配置
const routes = [
// 基础路由
{
path: '/',
name: 'Home',
component: Home
},
// 带参数的路由
{
path: '/user/:id',
name: 'User',
component: User,
props: true// 将路由参数作为 props 传递给组件
},
// 可选参数
{
path: '/user/:id?',
name: 'UserOptional',
component: User
},
// 多个参数
{
path: '/user/:id/post/:postId',
name: 'UserPost',
component: UserPost
},
// 通配符路由 (404 页面)
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: NotFound
}
]2. 嵌套路由
const routes = [
{
path: '/user/:id',
component: User,
children: [
// 空路径表示默认子路由
{ path: '', component: UserHome },
// 子路由
{ path: 'profile', component: UserProfile },
{ path: 'posts', component: UserPosts },
// 嵌套的动态路由
{ path: 'posts/:postId', component: UserPost }
]
}
]3. 命名视图
const routes = [
{
path: '/',
components: {
default: Home,
sidebar: Sidebar,
header: Header
}
}
]4. 路由懒加载
const routes = [
{
path: '/about',
name: 'About',
// 路由级别的代码分割
component: () =>import('@/views/About.vue')
},
// 带有自定义 chunk 名称
{
path: '/admin',
name: 'Admin',
component: () =>import(/* webpackChunkName: "admin" */'@/views/Admin.vue')
}
]1. 声明式导航
<template>
<!-- 基础导航 -->
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
<!-- 命名路由导航 -->
<router-link :to="{ name: 'User', params: { id: 123 } }">用户</router-link>
<!-- 带查询参数 -->
<router-link :to="{ path: '/search', query: { q: 'vue' } }">搜索</router-link>
<!-- 带 hash -->
<router-link :to="{ path: '/about', hash: '#team' }">关于团队</router-link>
<!-- 替换历史记录 -->
<router-link :to="{ path: '/home' }" replace>首页</router-link>
<!-- 自定义激活类名 -->
<router-link
to="/about"
active-class="active"
exact-active-class="exact-active"
>
关于
</router-link>
</template>2. 编程式导航
// 在组件中使用
exportdefault {
methods: {
navigateToUser() {
// 字符串路径
this.$router.push('/user/123')
// 对象形式
this.$router.push({ path: '/user/123' })
// 命名路由
this.$router.push({ name: 'User', params: { id: 123 } })
// 带查询参数
this.$router.push({ path: '/search', query: { q: 'vue' } })
// 替换当前历史记录
this.$router.replace({ name: 'Home' })
// 前进/后退
this.$router.go(-1) // 后退一步
this.$router.go(1) // 前进一步
this.$router.back() // 后退
this.$router.forward() // 前进
}
}
}3. 组合式 API 中的导航
import { useRouter, useRoute } from'vue-router'
exportdefault {
setup() {
const router = useRouter()
const route = useRoute()
const navigateToUser = () => {
router.push({ name: 'User', params: { id: 123 } })
}
// 获取当前路由信息
console.log(route.path)
console.log(route.params)
console.log(route.query)
return {
navigateToUser
}
}
}1. 全局守卫
// 全局前置守卫
router.beforeEach((to, from, next) => {
// to: 即将要进入的目标路由对象
// from: 当前导航正要离开的路由
// next: 一定要调用该方法来 resolve 这个钩子
console.log('导航到:', to.path)
console.log('来自:', from.path)
// 检查用户认证
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!isAuthenticated()) {
next({ name: 'Login' })
} else {
next()
}
} else {
next()
}
})
// 全局解析守卫
router.beforeResolve((to, from, next) => {
// 在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后调用
next()
})
// 全局后置钩子
router.afterEach((to, from) => {
// 这些钩子不会接受 next 函数也不会改变导航本身
console.log('导航完成')
// 更新页面标题
document.title = to.meta.title || 'Default Title'
})2. 路由独享守卫
const routes = [
{
path: '/admin',
component: Admin,
beforeEnter: (to, from, next) => {
// 只在进入该路由时调用
if (hasAdminPermission()) {
next()
} else {
next('/unauthorized')
}
}
}
]3. 组件内守卫
export default {
// 进入路由前
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不能获取组件实例 `this`,因为当守卫执行前,组件实例还没被创建
next(vm => {
// 通过 `vm` 访问组件实例
})
},
// 路由更新时
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 例如,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
next()
},
// 离开路由前
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
const answer = window.confirm('确定要离开吗?未保存的更改将会丢失。')
if (answer) {
next()
} else {
next(false)
}
}
}4. 组合式 API 中的守卫
import { onBeforeRouteLeave, onBeforeRouteUpdate } from'vue-router'
exportdefault {
setup() {
// 与 beforeRouteLeave 相同,无法访问 `this`
onBeforeRouteLeave((to, from) => {
const answer = window.confirm('确定要离开吗?')
if (!answer) returnfalse
})
// 与 beforeRouteUpdate 相同
onBeforeRouteUpdate(async (to, from) => {
if (to.params.id !== from.params.id) {
await fetchUser(to.params.id)
}
})
}
}1. useRouter 和 useRoute
import { useRouter, useRoute } from'vue-router'
import { computed, watch } from'vue'
exportdefault {
setup() {
const router = useRouter()
const route = useRoute()
// 响应式的路由参数
const userId = computed(() => route.params.id)
// 监听路由变化
watch(
() => route.params.id,
(newId, oldId) => {
console.log(`用户ID从 ${oldId} 变为 ${newId}`)
}
)
// 导航方法
const goToUser = (id) => {
router.push({ name: 'User', params: { id } })
}
return {
userId,
goToUser
}
}
}2. useLink
import { useLink } from'vue-router'
exportdefault {
props: {
to: {
type: [String, Object],
required: true
}
},
setup(props) {
const { href, navigate, isActive, isExactActive } = useLink(props)
return {
href,
navigate,
isActive,
isExactActive
}
}
}1. 路由元信息
const routes = [
{
path: '/admin',
component: Admin,
meta: {
requiresAuth: true,
title: '管理后台',
roles: ['admin', 'moderator']
}
}
]
// 在组件中访问
exportdefault {
created() {
console.log(this.$route.meta.title)
}
}2. 过渡效果
<template>
<router-view v-slot="{ Component, route }">
<transition :name="route.meta.transition || 'fade'">
<component :is="Component" :key="route.path" />
</transition>
</router-view>
</template>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
</style>3. 滚动行为
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 }
}
})4. 动态路由
// 添加路由
router.addRoute({
path: '/dynamic',
name: 'Dynamic',
component: DynamicComponent
})
// 添加嵌套路由
router.addRoute('Parent', {
path: 'child',
name: 'Child',
component: ChildComponent
})
// 删除路由
router.removeRoute('Dynamic')
// 检查路由是否存在
if (router.hasRoute('Dynamic')) {
console.log('路由存在')
}
// 获取所有路由
const routes = router.getRoutes()Router 实例方法
// 导航方法
router.push(location, onComplete?, onAbort?)
router.replace(location, onComplete?, onAbort?)
router.go(n)
router.back()
router.forward()
// 路由管理
router.addRoute(route)
router.addRoute(parentName, route)
router.removeRoute(name)
router.hasRoute(name)
router.getRoutes()
// 解析方法
router.resolve(location, current?, append?)
// 守卫方法
router.beforeEach(guard)
router.beforeResolve(guard)
router.afterEach(hook)
// 历史记录
router.isReady()
router.currentRouteRoute 对象属性
// 当前路由信息
route.path // 字符串,对应当前路由的路径
route.params // 对象,包含动态片段和全匹配片段的键值对
route.query // 对象,表示 URL 查询参数
route.hash // 字符串,当前路由的 hash 值
route.fullPath // 字符串,完整路径
route.matched // 数组,包含当前路由的所有嵌套路径片段的路由记录
route.name // 字符串,当前路由的名称
route.redirectedFrom // 如果存在重定向,即为重定向来源的路由的名字
route.meta // 路由元信息RouterLink Props
// RouterLink 组件属性
to // 目标路由的链接
replace // 是否替换当前历史记录
activeClass // 激活时的 CSS 类名
exactActiveClass // 精确激活时的 CSS 类名
custom // 是否阻止默认行为RouterView Props
// RouterView 组件属性
name // 命名视图的名称
route // 显式指定要渲染的路由完整的路由配置示例
// src/router/index.js
import { createRouter, createWebHistory } from'vue-router'
import { useAuthStore } from'@/stores/auth'
// 路由组件
const Home = () =>import('@/views/Home.vue')
const Login = () =>import('@/views/Login.vue')
const Dashboard = () =>import('@/views/Dashboard.vue')
const Profile = () =>import('@/views/Profile.vue')
const NotFound = () =>import('@/views/NotFound.vue')
const routes = [
{
path: '/',
name: 'Home',
component: Home,
meta: {
title: '首页'
}
},
{
path: '/login',
name: 'Login',
component: Login,
meta: {
title: '登录',
guest: true
}
},
{
path: '/dashboard',
name: 'Dashboard',
component: Dashboard,
meta: {
title: '仪表板',
requiresAuth: true
}
},
{
path: '/profile',
name: 'Profile',
component: Profile,
meta: {
title: '个人资料',
requiresAuth: true
}
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: NotFound,
meta: {
title: '页面未找到'
}
}
]
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
return { top: 0 }
}
})
// 全局前置守卫
router.beforeEach(async (to, from, next) => {
const authStore = useAuthStore()
// 设置页面标题
document.title = to.meta.title || 'My App'
// 检查认证状态
if (to.meta.requiresAuth && !authStore.isAuthenticated) {
next({ name: 'Login', query: { redirect: to.fullPath } })
} elseif (to.meta.guest && authStore.isAuthenticated) {
next({ name: 'Dashboard' })
} else {
next()
}
})
exportdefault router在组件中使用路由
<template>
<div>
<!-- 导航菜单 -->
<nav>
<router-link to="/" exact-active-class="active">首页</router-link>
<router-link to="/dashboard" active-class="active">仪表板</router-link>
<router-link to="/profile" active-class="active">个人资料</router-link>
</nav>
<!-- 路由视图 -->
<router-view v-slot="{ Component, route }">
<transition name="fade" mode="out-in">
<component :is="Component" :key="route.path" />
</transition>
</router-view>
</div>
</template>
<script>
import { useRouter, useRoute } from'vue-router'
import { computed } from'vue'
exportdefault {
setup() {
const router = useRouter()
const route = useRoute()
// 当前路径
const currentPath = computed(() => route.path)
// 导航方法
const navigateTo = (path) => {
router.push(path)
}
// 返回上一页
const goBack = () => {
router.go(-1)
}
return {
currentPath,
navigateTo,
goBack
}
}
}
</script>
<style>
.active {
color: #42b983;
font-weight: bold;
}
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
</style>1. 路由结构组织
// 按功能模块组织路由
const routes = [
// 公共路由
...publicRoutes,
// 用户相关路由
...userRoutes,
// 管理员路由
...adminRoutes,
// 404 路由(放在最后)
...notFoundRoutes
]2. 路由懒加载最佳实践
// 按页面分组
const UserRoutes = [
{
path: '/user',
component: () =>import(/* webpackChunkName: "user" */'@/layouts/UserLayout.vue'),
children: [
{
path: 'profile',
component: () =>import(/* webpackChunkName: "user" */'@/views/user/Profile.vue')
},
{
path: 'settings',
component: () =>import(/* webpackChunkName: "user" */'@/views/user/Settings.vue')
}
]
}
]3. 类型安全(TypeScript)
// 路由类型定义
import { RouteRecordRaw } from'vue-router'
declaremodule 'vue-router' {
interface RouteMeta {
title?: string
requiresAuth?: boolean
roles?: string[]
}
}
const routes: RouteRecordRaw[] = [
{
path: '/admin',
component: () =>import('@/views/Admin.vue'),
meta: {
title: '管理后台',
requiresAuth: true,
roles: ['admin']
}
}
]这份教程涵盖了 Vue Router 4.x 的所有主要 API 和使用方法,从基础配置到高级特性,应该能满足你的学习和开发需求
















