Vue Router 4 完整指南

目录


基础用法

1. 安装与引入

npm install vue-router@4
// main.js
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import App from './App.vue'
 
const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', component: Home },
    { path: '/about', component: About }
  ]
})
 
createApp(App).use(router).mount('#app')

2. 路由模式

模式方法URL 样式特点
HTML5 HistorycreateWebHistory()/user/123无 #,需服务器配置
HashcreateWebHashHistory()/#/user/123兼容性好,无需服务器配置
MemorycreateMemoryHistory()无 URL用于 SSR/无浏览器环境
import { createWebHistory, createWebHashHistory, createMemoryHistory } from 'vue-router'
 
// SPA 推荐使用 HTML5 History 模式
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes
})

3. 路由视图与链接

<!-- App.vue -->
<template>
  <!-- 路由出口 -->
  <router-view />
 
  <!-- 编程式导航 -->
  <button @click="goToAbout">关于</button>
 
  <!-- 声明式导航 -->
  <router-link to="/about">关于</router-link>
  <router-link :to="{ path: '/user', params: { id: 123 } }">用户</router-link>
</template>
 
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
const goToAbout = () => router.push('/about')
</script>

进阶用法

4. 动态路由参数

// 路由定义:使用 : 声明参数
{ path: '/user/:id', component: UserView }
{ path: '/article/:category/:id', component: ArticleView }
 
// 可选参数:使用 ?
{ path: '/user/:id?', component: UserView }
 
// 正则约束
{ path: '/user/:id(\\d+)', component: UserView }  // 只匹配数字
 
// 通配符
{ path: '/user/*', component: UserView }
{ path: '/:pathMatch(.*)*', component: NotFound }  // 404
<script setup>
import { useRoute } from 'vue-router'
 
const route = useRoute()
 
// 获取参数
console.log(route.params.id)    // /user/123 -> 123
console.log(route.query.search) // /?search=vue -> vue
console.log(route.hash)         // URL hash 部分
console.log(route.fullPath)     // 完整路径包含 query
</script>

5. 嵌套路由

{
  path: '/dashboard',
  component: DashboardLayout,
  children: [
    { path: '', redirect: 'overview' },  // 默认子路由
    { path: 'overview', component: Overview },
    { path: 'analytics', component: Analytics }
  ]
}
<!-- DashboardLayout.vue -->
<template>
  <div class="dashboard">
    <nav>...</nav>
    <!-- 子路由出口 -->
    <router-view />
  </div>
</template>

6. 命名路由

{ path: '/user/:id', name: 'user-profile', component: UserProfile }
 
// 编程式导航
router.push({ name: 'user-profile', params: { id: 123 } })
router.replace({ name: 'user-profile', params: { id: 123 } })
 
// 声明式
<router-link :to="{ name: 'user-profile', params: { id: 123 } }">用户</router-link>

7. 路由元信息 (Meta)

{
  path: '/admin',
  component: AdminPanel,
  meta: { requiresAuth: true, role: 'admin', title: '管理后台' }
}
 
{
  path: '/article/:id',
  component: ArticleView,
  meta: { keepAlive: true }  // 用于 keep-alive
}
// 在守卫中访问
router.beforeEach((to, from) => {
  if (to.meta.requiresAuth) {
    // 检查登录状态
  }
  document.title = to.meta.title || '默认标题'
})

8. 路由重定向

// 简单重定向
{ path: '/home', redirect: '/' }
 
// 命名路由重定向
{ path: '/home', redirect: { name: 'index' } }
 
// 函数重定向(可处理参数)
{ 
  path: '/user/:id', 
  redirect: to => {
    return { name: 'profile', params: { id: to.params.id } }
  }
}
 
// 别名
{ path: '/a', alias: '/b', component: A }  // /a 和 /b 都能访问

导航守卫

9. 全局守卫

// 全局前置守卫 - 最常用
router.beforeEach((to, from, next) => {
  // to: 目标路由对象
  // from: 当前路由对象
  // next: 确认导航的函数
 
  if (to.meta.requiresAuth && !isLoggedIn()) {
    next({ 
      name: 'login', 
      query: { redirect: to.fullPath }  // 登录后跳回
    })
  } else {
    next()  // 确认导航
    // next(false)  中断导航
    // next('/')     跳转到其他路由
    // next({ name: '404' })  跳转到命名路由
  }
})
 
// 全局解析守卫 - 在组件实例化前调用
router.beforeResolve((to, from, next) => {
  // 适合做数据预取
  next()
})
 
// 全局后置钩子 - 导航完成后调用
router.afterEach((to, from) => {
  // 适合做页面埋点、滚动到顶部
  window.scrollTo(0, 0)
})

10. 路由独享守卫

{
  path: '/profile',
  component: Profile,
  beforeEnter: (to, from, next) => {
    // 只对这条路由生效
    if (hasPermission()) {
      next()
    } else {
      next('/denied')
    }
  }
}
 
// 多个守卫
beforeEnter: [fn1, fn2, fn3]

11. 组件内守卫

<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
 
// 离开组件时调用
onBeforeRouteLeave((to, from) => {
  const answer = window.confirm('有未保存的更改,确定离开?')
  if (!answer) return false  // 取消导航
})
 
// 路由参数变化时调用(如同一个组件复用)
onBeforeRouteUpdate((to, from) => {
  // 重新获取数据
  fetchData(to.params.id)
})
</script>

12. 完整导航解析流程

导航触发
    ↓
1. 触发组件的 beforeRouteLeave 守卫(离开当前组件)
    ↓
2. 执行全局 beforeEach 守卫
    ↓
3. 在重用组件中调用 beforeRouteUpdate 守卫(路由参数变化)
    ↓
4. 执行路由独享 beforeEnter 守卫
    ↓
5. 解析异步路由组件
    ↓
6. 执行组件的 beforeRouteEnter 守卫
    ↓
7. 导航确认(next() 调用)
    ↓
8. 执行全局 beforeResolve 守卫
    ↓
9. 导航完成
    ↓
10. 触发全局 afterEach 钩子
    ↓
11. DOM 更新完成,beforeRouteEnter 的 next(vm => {}) 执行

高阶用法

13. 路由懒加载

// 方式1:动态导入(自动代码分割)
{ path: '/about', component: () => import('./views/About.vue') }
 
// 方式2:带命名 chunk(便于调试)
{ 
  path: '/about', 
  component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
}
 
// 方式3:带加载状态
const AsyncComponent = () => ({
  component: import('./views/Heavy.vue'),
  loading: LoadingComponent,
  error: ErrorComponent,
  delay: 200,
  timeout: 3000
})
 
// 方式4:预加载(prefetch)
const News = () => import(/* webpackPrefetch: true */ './views/News.vue')
 
// 方式5:懒加载多个路由
const routes = [
  path: '/',
  component: () => import('./views/Home.vue'),
  children: [
    { 
      path: 'dashboard',
      component: () => import(/* webpackChunkName: "dashboard" */ './views/Dashboard.vue')
    }
  ]
]

14. 路由动效过渡

<template>
  <router-view v-slot="{ Component, route }">
    <transition name="fade" mode="out-in">
      <component :is="Component" :key="route.path" />
    </transition>
  </router-view>
</template>
 
<style>
/* 渐变过渡 */
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
 
/* 滑动过渡 */
.slide-enter-active,
.slide-leave-active {
  transition: transform 0.3s ease;
}
.slide-enter-from {
  transform: translateX(100%);
}
.slide-leave-to {
  transform: translateX(-100%);
}
 
/* 缩放过渡 */
.zoom-enter-active,
.zoom-leave-active {
  transition: all 0.3s ease;
}
.zoom-enter-from,
.zoom-leave-to {
  opacity: 0;
  transform: scale(0.95);
}
</style>

15. 滚动行为控制

const router = createRouter({
  history: createWebHistory(),
  routes,
  scrollBehavior(to, from, savedPosition) {
    // 返回顶部
    return { top: 0 }
 
    // 滚动到锚点
    return { top: document.querySelector('#section').offsetTop }
 
    // 记住上次滚动位置(浏览器后退/前进)
    if (savedPosition) {
      return savedPosition
    }
 
    // 新页面滚动到顶部
    return { top: 0 }
 
    // 横向滚动
    return { left: 0, top: 0, behavior: 'smooth' }
  }
})

16. 动态路由操作

// 添加路由
router.addRoute({ 
  path: '/new-route', 
  name: 'new-route',
  component: NewComponent 
})
 
// 添加子路由
router.addRoute('parent-route-name', {
  path: 'child',
  component: ChildComponent
})
 
// 移除路由(按名称)
router.removeRoute('unused-route')
 
// 替换路由
router.replaceRoute('route-name')
 
// 路由是否存在
router.hasRoute('route-name')
 
// 获取所有路由
console.log(router.getRoutes())

17. 路由懒加载 + 守卫组合

{
  path: '/admin',
  component: () => import('./views/Admin.vue'),
  beforeEnter: (to, from, next) => {
    // 仅对此路由生效的守卫
    if (hasAdminPermission()) {
      next()
    } else {
      next('/403')
    }
  },
  meta: { requiresAuth: true }
}

18. 历史记录管理

// 替换当前记录(不保留历史)
router.replace('/new-path')
 
// 替换当前记录(编程式)
router.replace({ name: 'new-route', params: { id: 1 } })
 
// 前进/后退
router.go(1)   // 前进一页
router.go(-1)  // 后退一页
router.back()  // 后退
router.forward()  // 前进
 
// 监听历史变化
window.addEventListener('popstate', () => {
  // 浏览器前进/后退时触发
})

Composition API

19. 核心组合函数

<script setup>
import { 
  useRoute,      // 获取当前路由
  useRouter,     // 获取路由实例
  onBeforeRouteLeave,
  onBeforeRouteUpdate
} from 'vue-router'
 
const route = useRoute()
const router = useRouter()
 
// 路由信息
console.log(route.path)        // 路径
console.log(route.params)      // 参数
console.log(route.query)       // 查询参数
console.log(route.hash)        // hash
console.log(route.name)        // 路由名称
console.log(route.meta)         // 元信息
console.log(route.fullPath)    // 完整路径
 
// 导航方法
router.push('/about')
router.replace('/about')
router.go(1)
router.back()
 
// 监听 route 变化
import { watch } from 'vue'
watch(() => route.params.id, (newId) => {
  fetchData(newId)
})
</script>

20. 在 setup 中使用 beforeRouteEnter

<script setup>
import { onMounted } from 'vue'
 
onBeforeRouteEnter((to, from, next) => {
  // 组件实例还未创建,不能访问 `this`
  // 通过回调函数访问组件实例
  next(vm => {
    // vm 是组件实例,可访问 data、methods 等
    vm.fetchData()
  })
})
 
// 等效的 setup 写法
onMounted(() => {
  // 数据加载
})
</script>

21. 路由守卫中使用 async/await

router.beforeEach(async (to, from) => {
  // 可以 await 异步操作
  const user = await fetchUser()
  
  if (to.meta.requiresAuth && !user) {
    return { name: 'login', query: { redirect: to.fullPath } }
  }
  
  // 返回 true 或不返回 = 继续导航
  // 返回 false = 取消导航
})

实战技巧

22. 路由权限控制

// permission.js
const whiteList = ['/login', '/register', '/forgot-password']
 
router.beforeEach(async (to, from, next) => {
  const hasToken = getToken()
  
  if (hasToken) {
    if (to.name === 'login') {
      next({ path: '/' })
    } else {
      const hasPermission = checkPermission(to.meta.role)
      if (hasPermission) {
        next()
      } else {
        next('/403')
      }
    }
  } else {
    if (whiteList.includes(to.path)) {
      next()
    } else {
      next(`/login?redirect=${to.fullPath}`)
    }
  }
})

23. 路由划分与文件结构

src/
├── router/
│   ├── index.js           # 主入口
│   ├── routes/
│   │   ├── index.js       # 路由汇总
│   │   ├── modules/
│   │   │   ├── user.js    # 用户模块
│   │   │   ├── admin.js   # 管理模块
│   │   │   └── ...
│   └── asyncRoutes.js     # 动态路由
// routes/modules/user.js
export default [
  {
    path: '/user',
    name: 'UserLayout',
    component: () => import('@/views/user/Layout.vue'),
    children: [
      { path: 'profile', name: 'UserProfile', component: () => import('@/views/user/Profile.vue') },
      { path: 'settings', name: 'UserSettings', component: () => import('@/views/user/Settings.vue') }
    ]
  }
]

24. 页面缓存(Keep-Alive)

<!-- App.vue -->
<template>
  <router-view v-slot="{ Component, route }">
    <keep-alive :include="cachedComponents">
      <component :is="Component" :key="route.path" />
    </keep-alive>
  </router-view>
</template>
 
<script setup>
import { ref } from 'vue'
const cachedComponents = ref(['Home', 'Dashboard'])
</script>
// 配合 meta 使用
{
  path: '/dashboard',
  component: Dashboard,
  meta: { keepAlive: true }
}
<!-- Layout.vue -->
<keep-alive :include="cachedComponents">
  <router-view />
</keep-alive>
 
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
 
// 根据 meta 动态缓存
const cachedComponents = computed(() => {
  return route.matched
    .filter(r => r.meta.keepAlive)
    .map(r => r.components.default.name)
})
</script>

25. 路由状态保持与恢复

// 使用 sessionStorage 保存状态
router.beforeEach((to, from, next) => {
  // 保存滚动位置
  if (from.name && to.name !== from.name) {
    sessionStorage.setItem('scroll-' + from.name, JSON.stringify({
      top: window.scrollY,
      left: window.scrollX
    }))
  }
  next()
})
 
// 恢复滚动位置
scrollBehavior(to, from, savedPosition) {
  if (savedPosition) {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve(savedPosition)
      }, 100)
    })
  }
  return { top: 0 }
}

26. 微前端路由处理

// 独立子应用路由
const router = createRouter({
  history: createWebHistory('/child-app/'),  // 独立子路径
  routes
})
 
// 主应用使用 qiankun 加载子应用
// 子应用需要暴露 lifecycle
export async function mount(props) {
  router.start()
}

27. 常见问题处理

// 1. 导航到当前路由(触发更新)
router.push(route.fullPath)  // 使用 fullPath 而非 path
 
// 2. 路由参数变化不刷新组件
watch(() => route.params.id, (newId) => {
  // 手动处理数据更新
  fetchData(newId)
})
 
// 3. 连续点击路由报错
let isNavigating = false
router.beforeEach((to, from, next) => {
  if (isNavigating) return
  isNavigating = true
  next()
  setTimeout(() => { isNavigating = false }, 1000)
})
 
// 4. 带参数的路由重定向
{ 
  path: '/user/:id',
  redirect: to => {
    return '/profile/' + to.params.id
  }
}

28. TypeScript 类型支持

// router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
 
const routes: RouteRecordRaw[] = [
  {
    path: '/user/:id',
    name: 'User',
    component: () => import('@/views/User.vue'),
    props: true  // 将 params 作为 props 传递
  }
]
 
const router = createRouter({
  history: createWebHistory(),
  routes
})
 
export default router
 
// 在组件中使用
const props = defineProps<{
  id: string
}>()
// 扩展 RouteMeta
declare module 'vue-router' {
  interface RouteMeta {
    requiresAuth?: boolean
    role?: string
    title?: string
    keepAlive?: boolean
  }
}

附录:Router 实例属性与方法

Router 实例属性

属性类型说明
router.currentRouteRef<Route>当前路由对象
router.optionsRouteOptions原始路由配置
router.getRoutes()Route[]所有路由记录

Router 实例方法

方法说明
router.push(to)导航到新 URL(添加历史记录)
router.replace(to)替换当前 URL(不添加历史)
router.go(n)前进/后退 n 步
router.back()后退一页
router.forward()前进一页
router.addRoute(route)动态添加路由
router.addRoute(parentName, route)添加子路由
router.removeRoute(name)移除路由
router.hasRoute(name)检查路由是否存在
router.getRoutes()获取所有路由
router.resolve(to)解析路由对象

Route 对象属性

属性类型说明
route.pathstring路径
route.namestring | null路由名称
route.paramsRecord<string, string | string[]>动态参数
route.queryRecord<string, string | string[]>查询参数
route.hashstringhash 值
route.fullPathstring完整路径
route.matchedRouteRecord[]匹配的路由记录
route.metaRouteMeta元信息
route.redirectedFromstring | undefined重定向来源