4.1 基础配置
Vue Router 4 是 Vue 3 的官方路由库,API 设计与 Vue 3 的 Composition API 深度整合。
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
// createWebHistory:HTML5 History 模式(推荐,需要服务器配置)
// createWebHashHistory:Hash 模式(URL 带 #,无需服务器配置)
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView // 直接导入(首屏组件)
},
{
path: '/about',
name: 'about',
// 懒加载:路由被访问时才下载 JS
component: () => import('../views/AboutView.vue')
},
{
path: '/users/:id', // 动态路由参数
name: 'user-detail',
component: () => import('../views/UserDetail.vue'),
props: true // 路由参数作为 props 传入
},
{
path: '/:pathMatch(.*)*', // 404 页面(匹配所有未知路径)
name: 'not-found',
component: () => import('../views/NotFound.vue')
}
],
// 滚动行为控制
scrollBehavior(to, from, savedPosition) {
if (savedPosition) return savedPosition // 浏览器前进/后退时恢复位置
if (to.hash) return { el: to.hash, behavior: 'smooth' }
return { top: 0 }
}
})
export default router
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
4.2 动态路由与嵌套路由
const routes = [
{
path: '/dashboard',
component: () => import('@/layouts/DashboardLayout.vue'),
// 嵌套路由:DashboardLayout 中需要有 <RouterView />
children: [
{
path: '', // 默认子路由(/dashboard)
component: () => import('@/views/DashboardHome.vue')
},
{
path: 'profile', // /dashboard/profile
component: () => import('@/views/DashboardProfile.vue')
},
{
path: 'settings/:tab?', // 可选参数(? 表示可省略)
component: () => import('@/views/DashboardSettings.vue')
}
]
},
{
// 路由元信息:可以携带任意数据
path: '/admin',
meta: { requiresAuth: true, roles: ['admin'] },
component: () => import('@/views/Admin.vue')
}
]
4.3 路由守卫
路由守卫用于在路由导航时进行权限检查、数据预加载等操作。分为全局守卫、路由级守卫和组件内守卫:
import { useAuthStore } from '@/stores/auth'
// 全局前置守卫:每次导航前执行
router.beforeEach(async (to, from) => {
const auth = useAuthStore()
// 需要登录但未登录
if (to.meta.requiresAuth && !auth.isLoggedIn) {
return { name: 'login', query: { redirect: to.fullPath } }
}
// 检查角色权限
if (to.meta.roles && !to.meta.roles.includes(auth.userRole)) {
return { name: 'forbidden' }
}
// return undefined 或 true 表示放行
// return false 取消导航
// return { path: '/other' } 重定向
})
// 全局后置钩子(不影响导航,常用于更新页面标题)
router.afterEach((to) => {
document.title = (to.meta.title as string) || '我的应用'
})
<script setup lang="ts">
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
import { ref } from 'vue'
const isDirty = ref(false)
// 离开当前路由前(可用于提示保存)
onBeforeRouteLeave((to, from) => {
if (isDirty.value) {
const ok = window.confirm('有未保存的更改,确认离开?')
if (!ok) return false
}
})
// 路由参数变化时(同组件复用,如 /user/1 → /user/2)
onBeforeRouteUpdate(async (to, from) => {
const userId = to.params.id
await loadUser(userId)
})
</script>
4.4 组合式 API:useRouter 与 useRoute
<script setup lang="ts">
import { useRouter, useRoute } from 'vue-router'
import { computed, watch } from 'vue'
const router = useRouter() // 路由器实例(导航方法)
const route = useRoute() // 当前路由信息(响应式)
// 读取路由信息
const userId = computed(() => route.params.id as string)
const tab = computed(() => route.query.tab as string || 'overview')
// 侦听路由变化
watch(() => route.params.id, async (newId) => {
await loadUser(newId)
}, { immediate: true })
// 编程式导航
function goToUser(id: number) {
router.push({ name: 'user-detail', params: { id } })
}
function updateFilter(filter: string) {
// 更新查询参数而不添加历史记录
router.replace({ query: { ...route.query, filter } })
}
function goBack() {
router.back() // 等同于 router.go(-1)
}
</script>
4.5 路由懒加载与代码分割
路由懒加载是 SPA 性能优化的关键手段,Vite 会将每个懒加载路由打包为独立 chunk:
const routes = [
// 基础懒加载
{
path: '/heavy',
component: () => import('@/views/HeavyView.vue')
},
// 命名 chunk(便于分析)
{
path: '/admin',
component: () => import(/* webpackChunkName: "admin" */ '@/views/Admin.vue')
},
// 加载时显示 Loading 状态
{
path: '/dashboard',
component: {
// defineAsyncComponent 可以配置加载/错误组件
component: defineAsyncComponent({
loader: () => import('@/views/Dashboard.vue'),
loadingComponent: LoadingSpinner,
errorComponent: ErrorDisplay,
delay: 200, // 延迟显示 loading(避免闪烁)
timeout: 10000 // 超时时间
})
}
}
]
History 模式
使用 HTML5 History API,URL 格式为正常路径(/about),需要服务器配置将所有路径重定向到 index.html。
Hash 模式
URL 格式为 /#/about,不需要服务器配置,但 URL 不够美观,SEO 不友好。适合无法配置服务器的情况。
路由元信息 meta
在路由定义中通过 meta 字段附加任意数据(如 requiresAuth、title、roles),在守卫中通过 to.meta 访问。需要在 TypeScript 中通过模块扩展声明类型。
动态路由添加
router.addRoute() 可以在运行时动态添加路由,常用于根据用户权限动态注册路由的场景。
useRoute 的注意事项
useRoute() 返回的对象是响应式的,但其属性(如 route.params)本身是只读的。要侦听路由参数变化,使用 watch(() => route.params.id, handler) 而不是 watch(route.params, handler)(后者可能错过初始化)。