第12章 Vue Router 4

第十二章 Vue Router 4

Vue Router 是 Vue 官方出品的路由管理器,它是构建单页应用(SPA)的核心基础设施。当用户在页面上点击链接或改变 URL 时,Vue Router 负责找出匹配的路由规则,并渲染对应的组件。本章会详细介绍 Vue Router 4 的所有核心功能——从基础的路由配置到高级的导航守卫、懒加载和滚动行为控制。

12.1 SPA 与路由原理

12.1.1 单页面应用概念

传统的多页面应用(MPA)每次切换页面,浏览器都要向服务器请求一个新的 HTML 文件,页面会闪烁、重新加载整个页面。

单页面应用(SPA) 只需要一个 HTML 文件,通过 JavaScript 动态地替换页面内容来实现"页面切换"——页面永远不刷新,体验流畅,但页面 URL 会变化,给用户"在多个页面之间跳转"的感觉。

flowchart LR
    A["浏览器地址栏<br/>URL 变化"] --> B["Vue Router<br/>监听 URL 变化"]
    B --> C["匹配路由规则<br/>找到对应组件"]
    C --> D["组件切换<br/>页面内容更新"]
    D --> E["URL 历史栈<br/>浏览器后退/前进"]
    
    style A fill:#42b883,color:#fff
    style D fill:#f4a261,color:#fff

Vue Router 就是这个"根据 URL 变化渲染对应组件"的机制。它维护了一个 URL 到组件的映射表,当 URL 变化时,Vue Router 找到匹配的组件,渲染到 <router-view> 标签的位置。

12.1.2 history vs hash 模式

Vue Router 支持两种路由模式:history 模式hash 模式

hash 模式:URL 里带 #,比如 example.com/#/user/123# 后面的内容叫哈希(hash),浏览器不会把哈希部分发送给服务器——所以即使服务器没有配置任何路由,只要返回 index.html,Vue Router 都能正常工作。

1
2
3
4
# hash 模式的 URL
https://example.com/#/user/123
https://example.com/#/products/5
https://example.com/#/about

history 模式:URL 里没有 #,看起来像普通的多页面 URL:

1
2
3
4
# history 模式的 URL
https://example.com/user/123
https://example.com/products/5
https://example.com/about

history 模式需要服务器配合——因为当用户直接访问 example.com/user/123 时,浏览器会向服务器请求 /user/123 这个路径的 HTML 文件,服务器如果没有配置,会返回 404。需要在服务器上配置把所有路由都回退到 index.html

12.1.3 三种模式对比(hash / history / abstract)

模式URL 样子是否需要服务器配置微信/内网兼容性推荐场景
hash/#/user❌ 不需要✅ 好(内网穿透简单)内部工具、简单后台
history/user✅ 需要❌ 需要额外配置正式上线项目、对外网站
abstract无 URLN/AN/A非浏览器环境(如 Node.js 测试)

12.2 快速上手

12.2.1 安装与引入

1
pnpm add vue-router

12.2.2 基础配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'

// 路由配置数组:每一条记录对应一个 URL → 组件的映射
const routes = [
  {
    path: '/',                        // URL 路径(必填)
    name: 'Home',                     // 路由名称(可选,但推荐写,方便用 name 而非 path 做跳转)
    component: () => import('../views/Home.vue')  // 懒加载:访问 / 时才加载这个组件
    // component: Home                  // 非懒加载写法:直接 import 进来(首屏就会加载)
    // redirect: '/home'               // 重定向:访问 / 时自动跳转到 /home
    // props: true                     // 把 route.params 当作 props 传给组件
    // meta: { requiresAuth: true }    // 自定义数据:权限、标题等(详见路由守卫章节)
    // children: []                    // 子路由:嵌套路由(详见嵌套路由章节)
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('../views/About.vue')
  },
  // 动态路由:/:id 匹配任意路径,id 会成为 route.params.id
  {
    path: '/user/:id',              // :id 是动态段,匹配 /user/123、/user/abc 等
    name: 'UserProfile',
    component: () => import('../views/UserProfile.vue')
    // 组件里通过 const route = useRoute(); route.params.id 获取 id 值
  },
  // 通配符:匹配所有未定义的路径(通常用于 404 页面)
  {
    path: '/:pathMatch(.*)*',      // 正则:匹配任意路径
    name: 'NotFound',
    component: () => import('../views/NotFound.vue')
  }
]

// 创建路由实例
const router = createRouter({
  // history:路由模式
  // createWebHistory('/') → History 模式(URL 更美观,适合有服务器配置的项目)
  // createWebHashHistory('/') → Hash 模式(URL 带 #,无需服务器配置,适合静态托管)
  history: createWebHistory(import.meta.env.BASE_URL),
  routes  // routes:路由配置数组,上面的 routes 常量
})

export default router
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(router)  // 注册路由插件

app.mount('#app')

<router-link>:用于在页面上生成链接,点击时不刷新页面,而是触发路由切换。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<!-- App.vue -->
<script setup>
import { RouterLink, RouterView } from 'vue-router'
</script>

<template>
  <nav>
    <!-- router-link 会自动给当前匹配的链接加 .router-link-active class -->
    <RouterLink to="/">首页</RouterLink>
    <RouterLink to="/about">关于</RouterLink>
    <RouterLink to="/user/123">用户</RouterLink>
  </nav>

  <!-- router-view 是路由视图——匹配的组件会渲染在这里 -->
  <RouterView />
</template>

<RouterLink> 的属性:

1
2
3
4
5
6
7
8
<RouterLink
  to="/about"
  replace               <!--  replaceState 而不是 pushState不留历史记录 -->
  active-class="active" <!-- 自定义激活 class 默认是 router-link-active-->
  exact-active-class="exact-active"  <!-- 精确匹配时的 class -->
>
  关于
</RouterLink>

12.2.4 useRouter / useRoute 组合式 API

Vue Router 4 提供了两个组合式 API:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import { useRoute, useRouter } from 'vue-router'

// useRouter:路由实例,可以编程式导航
const router = useRouter()

// useRoute:当前路由信息(URL 参数、匹配规则等)
const route = useRoute()

// 编程式导航
router.push('/user/123')    // 跳转(会留下历史记录)
router.replace('/about')    // 替换(不留下历史记录)
router.go(-1)              // 后退
router.forward()           // 前进

12.3 路由定义与参数

12.3.1 静态路由

1
2
3
4
5
const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About },
  { path: '/contact', component: Contact }
]

12.3.2 动态路由(params 路径参数)

用冒号 : 来定义动态路径参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const routes = [
  // /user/123 匹配这个路由
  { path: '/user/:id', component: UserDetail },

  // 支持多个参数
  { path: '/user/:userId/post/:postId', component: PostDetail },

  // 参数可选
  { path: '/user/:id?', component: UserProfile },

  // 参数有正则约束
  { path: '/user/:id(\\d+)', component: UserByNumber }
]

12.3.3 query 参数(URL 查询参数)

query 参数是 URL 中 ? 后面的部分,比如 /search?keyword=vue&page=1

1
2
3
4
5
<!-- 跳转时带 query 参数 -->
<RouterLink to="/search?keyword=vue&page=1">搜索</RouterLink>

<!-- 编程式跳转 -->
router.push({ path: '/search', query: { keyword: 'vue', page: 1 } })

12.3.4 嵌套路由

children 属性定义嵌套路由:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const routes = [
  {
    path: '/user',
    component: UserLayout,
    children: [
      { path: '', redirect: '/user/profile' },  // 默认子路由
      { path: 'profile', component: UserProfile },  // /user/profile
      { path: 'settings', component: UserSettings }  // /user/settings
    ]
  }
]

12.3.5 多级嵌套

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
  path: '/dashboard',
  component: DashboardLayout,
  children: [
    {
      path: 'analytics',
      component: Analytics,
      children: [
        { path: 'realtime', component: RealtimeAnalytics },
        { path: 'historical', component: HistoricalAnalytics }
      ]
    }
  ]
}

12.3.6 命名路由

给路由起个名字,编程式跳转时用名字而不是路径:

1
2
3
4
5
6
7
8
9
const routes = [
  { path: '/user/:id', name: 'UserDetail', component: UserDetail }
]

// 用名字跳转
router.push({ name: 'UserDetail', params: { id: '123' } })
// URL 变成 /user/123

// 比直接写路径更可靠,路径变了名字不用改

12.4 路由参数获取

12.4.1 useRoute 获取参数(params / query)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import { useRoute } from 'vue-router'

const route = useRoute()

// 获取 params 参数
console.log(route.params.id)  // '123'

// 获取 query 参数
console.log(route.query.keyword)  // 'vue'

// 获取路由名
console.log(route.name)  // 'UserDetail'

12.4.2 路由参数变化检测

当 URL 的 params 参数变化时(如从 /user/1 切换到 /user/2),组件不会重新创建(因为是同一个路由规则),需要用 watch 来监听:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import { watch } from 'vue'
import { useRoute } from 'vue-router'

const route = useRoute()

// 监听 params 变化
watch(
  () => route.params.id,
  (newId) => {
    console.log('用户 ID 从变了:', newId)
    fetchUser(newId)
  }
)

12.4.3 路由 Props 传递

可以用 props 把路由参数作为组件的 props 传递:

1
2
3
4
5
// 路由配置:启用 props
{ path: '/user/:id', component: UserDetail, props: true }

// 组件里直接用 props.id,而不是 route.params.id
defineProps<{ id: string }>()

12.4.4 $route 对象结构解析

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
interface RouteLocationNormalized {
  path: string        // '/user/123'
  name: string | null // 'UserDetail'
  params: Record<string, string | string[]>
  query: Record<string, string | string[]>
  hash: string         // '#section'
  fullPath: string     // '/user/123?page=1#section'
  meta: RouteMeta      // 路由元信息
  redirectedFrom: string | null
}

12.5 编程式导航

12.5.1 router.push / replace / go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const router = useRouter()

// push:跳转到指定路径(会留下历史记录)
router.push('/user/123')
router.push({ path: '/user/123' })
router.push({ name: 'UserDetail', params: { id: '123' }, query: { tab: 'info' } })

// replace:替换当前历史记录(不会留下历史记录)
router.replace('/about')

// go:在历史记录中前进或后退
router.go(-1)    // 后退一页
router.go(1)    // 前进一页
router.go(-2)   // 后退两页

12.5.2 命名视图(多个 router-view)

如果一个页面有多个独立区域需要渲染不同的组件,可以用命名视图

1
2
3
4
5
6
7
8
<!-- App.vue -->
<template>
  <RouterView name="header" />   <!-- 命名视图header -->
  <main>
    <RouterView />                <!-- 默认视图 -->
  </main>
  <RouterView name="footer" />    <!-- 命名视图footer -->
</template>
1
2
3
4
5
6
7
8
9
// 路由配置:给每个视图分配不同的组件
{
  path: '/',
  components: {
    default: () => import('./views/Main.vue'),
    header: () => import('./views/Header.vue'),
    footer: () => import('./views/Footer.vue')
  }
}

12.5.3 别名与重定向

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const routes = [
  // redirect:访问 /home 时自动跳转到 /
  { path: '/home', redirect: '/' },

  // redirect 到命名路由
  { path: '/old-user/:id', redirect: (to) => `/user/${to.params.id}` },

  // alias:两个 URL 都能访问同一个组件
  { path: '/about', component: About, alias: '/关于' }
]

12.6 路由守卫

12.6.1 全局前置守卫(beforeEach)

最常用的守卫,在每次路由切换前触发:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(),
  routes: [...]
})

// 全局前置守卫:每次路由跳转之前都会执行
// 适合做:登录验证、权限检查、页面标题设置等
router.beforeEach((to, from) => {
  // to:目标路由对象(包含 path、params、query、meta 等)
  //     例如:to.path = '/admin',to.meta.requiresAuth = true
  // from:来源路由对象(上一页是什么)

  // beforeEach 必须返回一个值来决定是否放行:
  // return true  → 允许跳转,继续执行后续流程
  // return false → 阻止跳转,停留在当前页面
  // return '/login' 或 return { name: 'Login' } → 跳转到指定路径
  // return new Error('xxx') → 取消跳转并抛出错误

  // 示例:检查目标页面是否需要登录
  if (to.meta.requiresAuth) {
    // to.meta.requiresAuth 是我们在路由配置里自定义的数据(meta 字段)
    // meta 可以存放任意自定义数据:requiresAuth、roles、title 等
    const token = localStorage.getItem('token')
    if (!token) {
      // 没有 token,跳转到登录页,并把用户想访问的页面作为 redirect 参数带过去
      return { name: 'Login', query: { redirect: to.fullPath } }
    }
  }

  // 动态设置页面标题(每个路由的 meta 里设置 title 即可)
  if (to.meta.title) {
    document.title = `${to.meta.title} - 我的网站`
  }

  return true  // 放行
})

12.6.2 全局解析守卫(beforeResolve)

在所有组件内守卫和异步路由组件解析之后、导航确认之前触发:

1
2
3
4
5
router.beforeResolve((to, from) => {
  // 适合在这里做数据预获取
  console.log('路由解析完成,即将跳转')
  return true
})

12.6.3 全局后置钩子(afterEach)

导航完成后触发,适合做页面埋点:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
router.afterEach((to, from) => {
  // 没有返回值,用于"事后处理"
  console.log('跳转完成,从', from.path, '到', to.path)

  // 发送 PV 统计
  fetch('/api/analytics', {
    method: 'POST',
    body: JSON.stringify({ path: to.path, referrer: from.path })
  })
})

12.6.4 路由独享守卫(beforeEnter)

写在路由配置里,只在进入这个路由时触发:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  path: '/admin',
  component: AdminPanel,
  beforeEnter: (to, from) => {
    // 只有访问 /admin 时才检查权限
    if (!isAdmin()) {
      return '/login'
    }
    return true
  }
}

12.6.5 组件内守卫(beforeRouteEnter / Update / Leave)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
export default {
  beforeRouteEnter(to, from) {
    // 在渲染该组件的路由确认前调用
    // 不能访问 this(组件实例还没创建)
    // 可以用 next(vm => { vm.xxx }) 访问组件实例
    next(vm => {
      console.log('组件已挂载,vm 是组件实例', vm)
    })
  },

  beforeRouteUpdate(to, from) {
    // 路由参数变化时调用(如 /user/1 -> /user/2)
    // 可以访问 this
    console.log('路由参数变化了', to.params.id)
  },

  beforeRouteLeave(to, from) {
    // 离开该路由时调用
    const answer = window.confirm('有未保存的内容,确定离开吗?')
    if (!answer) return false
  }
}

12.6.6 setup 组件中的路由守卫(onBeforeRouteEnter / onBeforeRouteUpdate / onBeforeRouteLeave,Vue 3.5+)

Vue 3.5 引入了组合式 API 版本的路由守卫:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import { onBeforeRouteEnter, onBeforeRouteUpdate, onBeforeRouteLeave } from 'vue-router'

onBeforeRouteEnter((to, from) => {
  console.log('进入路由')
})

onBeforeRouteUpdate((to, from) => {
  console.log('路由更新了', to.params.id)
})

onBeforeRouteLeave((to, from) => {
  if (hasUnsavedChanges.value) {
    return '有未保存的更改,确定离开?'
  }
})

12.6.7 守卫执行顺序

1. 路由切换触发
     ↓
2. 全局 beforeEach(按注册顺序,每个都执行完)
     ↓
3. 路由独享 beforeEnter(按配置顺序)
     ↓
4. 组件 beforeRouteEnter
     ↓
5. 全局 beforeResolve
     ↓
6. 导航确认,DOM 更新
     ↓
7. 全局 afterEach(后置钩子,无权阻止)

12.7 路由元信息(meta)

路由的 meta 字段可以存储任意自定义信息,用来实现路由级别的权限、标题、缓存策略等:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  path: '/admin',
  component: AdminPanel,
  meta: {
    requiresAuth: true,     // 需要登录才能访问
    roles: ['admin'],       // 需要 admin 角色
    title: '管理后台',       // 页面标题(配合全局守卫设置 document.title)
    keepAlive: true        // 是否需要 KeepAlive 缓存
  }
}

常见 meta 字段及用途:

meta 字段类型用途
requiresAuthboolean是否需要登录
rolesstring[]允许访问的角色列表
titlestring页面标题
keepAliveboolean是否缓存组件
breadcrumbBreadcrumbItem[]面包屑导航数据
affixboolean是否固定在标签栏(KeepAlive 场景)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 在全局守卫里读取 meta
router.beforeEach((to, from) => {
  // to.meta 是 RouteMeta 类型(详见下节),访问方式和普通对象一样
  if (to.meta.requiresAuth) {
    // 检查登录状态
  }

  if (to.meta.roles) {
    // 检查用户角色是否匹配
    const userRole = getUserRole()
    if (!to.meta.roles.includes(userRole)) {
      return '/403'  // 无权限,跳转到 403 页面
    }
  }

  // 动态设置页面标题
  if (to.meta.title) {
    document.title = `${to.meta.title} - 我的网站`
  }
})

RouteMeta 类型定义(TypeScript 推荐):

用 TypeScript 时,建议给 meta 定义一个接口,这样 IDE 会有类型提示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// src/types/router.d.ts
import 'vue-router'

// 扩展 RouteMeta 接口,给 meta 字段添加类型
declare module 'vue-router' {
  interface RouteMeta {
    requiresAuth?: boolean    // 是否需要登录
    roles?: string[]          // 允许的角色
    title?: string            // 页面标题
    keepAlive?: boolean       // 是否 KeepAlive
  }
}

这样 to.meta.requiresAuth 就有了正确的类型,IDE 也能提供自动补全。

12.8 高级用法

12.8.1 滚动行为控制

路由切换后,页面会保持在切换前的滚动位置。你可以通过 scrollBehavior 自定义这个行为——比如让页面每次切换都滚到顶部,或者记住用户在某个页面的滚动位置。

常见场景

  • 点击"回到顶部"链接后,切换路由时自动滚到顶部
  • 用户在列表页滚动到一半,点击进入详情页,再点返回,能回到列表页的原来位置(savedPosition
  • 点击锚点链接(如 #comments),跳转到页面特定位置
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const router = createRouter({
  history: createWebHistory(),
  routes: [...],

  // scrollBehavior:控制路由切换后的页面滚动位置
  // to:目标路由,from:来源路由,savedPosition:浏览器前进/后退时的位置
  scrollBehavior(to, from, savedPosition) {
    // 场景一:用户点击浏览器的前进/后退按钮,恢复到之前的滚动位置
    // 这是最符合直觉的行为——返回上一页时,滚动位置应该和离开时一样
    if (savedPosition) {
      return savedPosition
    }

    // 场景二:目标路由 URL 里有 hash(如 /article#comments),滚动到对应锚点
    // el: to.hash → 找到 id 为 hash 的元素,behavior: 'smooth' → 平滑滚动
    if (to.hash) {
      return { el: to.hash, behavior: 'smooth' }
    }

    // 场景三:默认行为——每次路由切换都滚动到页面顶部
    // top: 0 → 距离页面顶部 0px(即最顶部)
    return { top: 0, behavior: 'smooth' }
  }
})

12.8.2 路由懒加载(动态 import)

把每个路由组件拆分成独立的 JS 文件,用户访问时才加载:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 不用懒加载:所有组件一次性加载(首屏慢)
import Home from '../views/Home.vue'

// 懒加载:路由匹配时才加载(首屏快)
const Home = () => import('../views/Home.vue')

// 带名字的懒加载(webpack)
const Home = () => import(/* webpackChunkName: "home" */ '../views/Home.vue')

const routes = [
  { path: '/', component: () => import('../views/Home.vue') },
  { path: '/about', component: () => import('../views/About.vue') }
]

12.8.3 路由过渡动画

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<template>
  <RouterView v-slot="{ Component }">
    <Transition name="fade" mode="out-in">
      <component :is="Component" :key="route.path" />
    </Transition>
  </RouterView>
</template>

<style>
.fade-enter-active, .fade-leave-active { transition: opacity 0.3s; }
.fade-enter-from, .fade-leave-to { opacity: 0; }
</style>

12.8.4 导航故障处理(NavigationFailureType)

Vue Router 4 引入了更精确的导航故障处理:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import { createRouter, NavigationFailureType } from 'vue-router'

router.push('/user/123').catch((err) => {
  if (err.type === NavigationFailureType.redirected) {
    console.log('导航被重定向了')
  }
  if (err.type === NavigationFailureType.aborted) {
    console.log('导航被 beforeEach 阻止了')
  }
  if (err.type === NavigationFailureType.cancelled) {
    console.log('导航被新的导航取消了')
  }
  if (err.type === NavigationFailureType.duplicated) {
    console.log('导航到当前页面(重复导航)')
  }
})

12.8.5 history 模式服务器配置(nginx)

1
2
3
location / {
  try_files $uri $uri/ /index.html;
}

try_files 的意思是:先找对应的文件,找不到就找目录,都找不到就返回 index.html——这样 SPA 的路由就能正常工作了。

12.8.6 通配符与 404

1
2
3
// 放在路由配置的最后
{ path: '/:pathMatch(.*)*', component: NotFound }  // 匹配所有未定义的路由
{ path: '/:pathMatch(.*)', component: NotFound }    // 不带 * 的版本只能匹配单层

12.9 路由进阶

12.9.1 路由分组与批量导入

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// src/router/modules/home.ts
export default [
  { path: '/', name: 'Home', component: () => import('@/views/home/index.vue') },
  { path: '/about', name: 'About', component: () => import('@/views/home/About.vue') }
]

// src/router/modules/user.ts
export default [
  { path: '/user/:id', name: 'UserDetail', component: () => import('@/views/user/Detail.vue') },
  { path: '/user/settings', name: 'UserSettings', component: () => import('@/views/user/Settings.vue') }
]

// router/index.ts 合并
import homeRoutes from './modules/home'
import userRoutes from './modules/user'

const routes = [
  ...homeRoutes,
  ...userRoutes,
  { path: '/:pathMatch(.*)*', component: () => import('@/views/NotFound.vue') }
]

12.9.2 动态路由注册

1
2
3
4
5
6
7
// 模拟:从服务器获取权限路由配置
const permissionRoutes = await fetch('/api/routes').then(r => r.json())

// 动态添加路由
permissionRoutes.forEach((route: RouteRecordRaw) => {
  router.addRoute(route)
})

本章小结

本章我们系统学习了 Vue Router 4 的核心知识:

  • SPA 路由原理:history vs hash 两种模式,URL 变化通过 JavaScript 切换组件而不是刷新页面。
  • 快速上手:创建路由实例,router-link 和 router-view 的基本用法。
  • 路由参数:params 动态路径参数、query 查询参数、嵌套路由、多级路由。
  • 编程式导航router.pushrouter.replacerouter.go,命名视图和重定向。
  • 路由守卫:全局前置/解析/后置守卫、路由独享守卫、组件内守卫、组合式 API 守卫(Vue 3.5+),完整的守卫执行顺序。
  • 路由 meta 元信息:存储任意自定义数据,导航守卫中读取。
  • 高级用法:滚动行为、路由懒加载、过渡动画、导航故障处理、history 模式服务器配置、动态路由注册。

下一章我们会学习 Pinia 状态管理——Vue 官方推荐的全新状态管理方案,它比 Vuex 更简洁、更 TypeScript 友好,是 Vue 3 项目的首选状态管理工具!