摘要: 从本篇开始,我们的待办应用将摆脱"单页面"的束缚,正式进化为拥有多个视图、可前进后退的现代单页应用(SPA)。前端路由正是实现这一切的核心机制。本文将先为你揭开路由的神秘面纱,从 Hash 和 History 两种模式的工作原理讲起,然后逐步带你安装 Vue Router、定义路由表、使用 <router-link> 和 <router-view> 完成页面跳转。接着,我们将深入动态路由、嵌套路由、命名视图、编程式导航,以及至关重要的路由守卫和元信息。配合 TypeScript 的类型增强,你会学会如何构建安全、可维护的应用导航体系。最终,我们会将之前的 Todo 应用拆分为列表页、详情页和设置页,用路由串联它们,真正感受 SPA 的流畅体验。
一、前端路由是什么?为什么要用路由?
1.1 从"多页"到"单页"的进化
还记得你初学 HTML 时所做的网页吗?点击一个链接,浏览器向服务器请求新的 HTML 文件,整个页面重新加载------这就是多页应用(MPA)。这种模式在用户体验上存在明显割裂感:每次跳转都有白屏,状态也难以保持。
后来,Ajax 技术让局部更新成为可能,但 URL 并不会变化,用户无法通过浏览器的"前进/后退"回到之前的状态,也无法把当前视图加入书签。前端路由 应运而生:它让 URL 变化时不向服务器发送请求,而是由 JavaScript 接管,动态替换页面的部分内容。这便是**单页应用(SPA)**的基础。
1.2 两种路由模式:Hash vs History
Vue Router 支持两种模式,但它们的原理你最好心知肚明。
-
Hash 模式 URL 中带有一个
#号,例如http://example.com/#/list。#及其后面的部分被称为哈希(hash)。浏览器不会把#后的内容发送给服务器,它的变化不会触发页面刷新,但会被记录在浏览历史中。Vue Router 默认使用 hash 模式,因为它兼容所有浏览器,部署也最省心。 -
History 模式 利用 HTML5 的
history.pushState和history.replaceStateAPI,可以实现完全不带#的漂亮 URL,如http://example.com/list。这种模式对 SEO 更友好,用户也看着更自然。但它需要后端配合:因为刷新页面时,浏览器会向服务器请求这个路径,服务器必须始终返回index.html,否则会出现 404。
Vue Router 3.x 默认使用 hash,4.x(对应 Vue 3)也支持通过 createWebHistory 切换到 history 模式。对于你的项目,初期可直接使用 hash 模式,后续上线再配置 nginx 等后端支持。
二、Vue Router 初体验
2.1 创建 Vite + Vue3 项目
TypeScript
pnpm create vite my-vue-router-app --template vue-ts
cd my-vue-router-app
pnpm install
2.2 安装与创建路由
在项目中安装 vue-router(注意,Vue 3 必须使用 4.x 版本):
TypeScript
pnpm add vue-router@4
接着创建路由文件,一般放在 src/router/index.ts:
TypeScript
import { createRouter, createWebHashHistory } from 'vue-router'
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'home',
component: () => import('../views/HomePage.vue'),
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutPage.vue'),
},
{
path: '/:pathMatch(.*)*',
name: 'not-found',
component: () => import('../views/NotFound.vue'),
}
]
const router = createRouter({
history: createWebHashHistory(),
routes,
})
export default router
这里使用了动态导入 () => import(...),Vite 会将其自动拆分为独立的 chunk,实现路由级别的懒加载,首屏更快。
2.3 在 Vue 应用中安装路由
修改 src/main.ts,通过 app.use(router) 注册:
TypeScript
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router' // 引入路由
const app = createApp(App)
app.use(router) // 注册路由
app.mount('#app')
app.use() 你之前可能用过,它是 Vue 插件系统的标准入口。Vue Router 作为插件被安装后,整个应用都可以访问路由实例。
2.4 放置路由出口:<router-view>
现在修改 App.vue,删除之前的内容,换成 <router-view>:
TypeScript
<template>
<div id="app">
<!-- 导航栏 -->
<nav class="nav">
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
</nav>
<!-- 路由页面渲染在这里 -->
<div class="container">
<router-view />
</div>
</div>
</template>
<style scoped>
.nav {
padding: 16px;
background: #f5f5f5;
margin-bottom: 20px;
}
.nav a {
margin-right: 16px;
text-decoration: none;
color: #333;
}
.nav a:hover {
color: #0066cc;
}
.container {
padding: 0 16px;
}
</style>
<router-view> 是一个动态组件,会根据当前 URL 渲染匹配的页面组件。<router-link> 则会被渲染为一个 <a> 标签,点击时不会刷新页面,只触发前端路由切换。
2.4 编写简单页面
在 src/views/ 下创建三个文件:
src/views/HomePage.vue(首页)
TypeScript
<template>
<div>
<h1>🏠 首页</h1>
<p>欢迎使用 Vue 3 + Vue Router 4</p>
</div>
</template>
src/views/AboutPage.vue(关于页)
TypeScript
<template>
<div>
<h1>ℹ️ 关于页面</h1>
<p>这是一个单页应用(SPA)</p>
</div>
</template>
src/views/NotFound.vue(404 页面)
TypeScript
<template>
<div>
<h1>❌ 404 页面不存在</h1>
<p><router-link to="/">返回首页</router-link></p>
</div>
</template>
运行 pnpm dev,点击导航链接,页面内容无刷新切换,URL 也会自动变成 /#/ 和 /#/about。你的第一个 SPA 已经诞生!



三、动态路由:路径中的变量
真实的业务中,路由往往携带变量。比如商品详情页需要商品 ID,/product/123。Vue Router 的动态路由 用 : 定义参数。
3.1 定义动态路由
在src/router/index.ts文件中添加:
TypeScript
{
path: '/todo/:id',
name: 'todo-detail',
component: () => import('../views/TodoDetail.vue')
}
:id 表示动态部分,可以匹配任意字符串(不包括 /)。如果希望参数可选,可以在后面加 ?:/:id?。

3.2 获取并监听路由参数
创建【待办详情页】src/views/TodoDetail.vue,在组件中,通过 useRoute 获取参数:
TypeScript
<script setup lang="ts">
import { useRoute } from 'vue-router'
import { watch } from 'vue'
// 1. 获取当前路由
const route = useRoute()
// 2. 拿到动态参数 id
const todoId = route.params.id
// 3. 监听参数变化(从 /todo/1 → /todo/2 会触发)
watch(
() => route.params.id,
(newId) => {
console.log('ID 变了:', newId)
// 这里可以重新请求接口拿数据
}
)
</script>
<template>
<div>
<h1>待办详情</h1>
<h3>当前路由参数:{{ todoId }}</h3>
<p>你正在查看第 {{ todoId }} 号任务</p>
<br />
<router-link to="/">← 返回首页</router-link>
</div>
</template>
如果你想让 TypeScript 知道 id 一定是字符串(因为路径是 :id),可以利用路由的类型声明进行增强。Vue Router 提供了类型扩展接口,但最简单的方式就是使用非空断言或类型转换。
3.3 使用 useRoute 和 useRouter 的区别
-
useRoute()返回当前路由的响应式对象,包含params、query、hash、meta等,用于读取信息。 -
useRouter()返回路由器实例,用于编程式导航(如push、replace)。
TypeScript
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
function goToDetail(id: number) {
router.push({ name: 'todo-detail', params: { id } })
}
3.4 修改主页文件
src/views/HomePage.vue
TypeScript
<script setup lang="ts">
import { useRouter } from 'vue-router'
// 获取路由实例(用来跳转)
const router = useRouter()
// 编程式导航(点击按钮跳转)
function goDetail(id: number) {
router.push({
name: 'todo-detail',
params: { id: id }
})
}
</script>
<template>
<div>
<h1>首页 → 待办列表</h1>
<!-- 方式1:router-link 跳转 -->
<div>
<p>方式1:标签跳转</p>
<router-link to="/todo/1">查看 1 号任务</router-link>
<br />
<router-link to="/todo/2">查看 2 号任务</router-link>
</div>
<br />
<!-- 方式2:点击事件跳转 -->
<div>
<p>方式2:按钮编程式跳转</p>
<button @click="goDetail(100)">查看 100 号任务</button>
<button @click="goDetail(200)">查看 200 号任务</button>
</div>
</div>
</template>
3.5 App.vue 加导航
修改src/App.vue
TypeScript
<template>
<div>
<nav>
<router-link to="/">首页</router-link> |
<router-link to="/about">关于</router-link> |
<router-link to="/todo/1">任务1</router-link> |
<router-link to="/todo/66">任务66</router-link>
</nav>
<hr />
<router-view />
</div>
</template>


四、嵌套路由与命名视图
4.1 嵌套路由
当页面有公共布局(如侧边栏、顶栏)时,我们希望子页面嵌套在布局内部切换。Vue Router 通过 children 配置实现:
TypeScript
const routes: RouteRecordRaw[] = [
{
path: '/user',
component: () => import('../views/UserLayout.vue'),
children: [
{
path: '', // 默认子路由
component: () => import('../views/UserProfile.vue')
},
{
path: 'settings',
component: () => import('../views/UserSettings.vue')
}
]
}
]
4.2 命名视图
有时候,一个页面需要同时展示多个同级组件(比如侧边栏和主内容同时变化)。命名视图可以使用 components 替代 component,并给 <router-view> 指定 name:
TypeScript
{
path: '/dashboard',
components: {
default: () => import('../views/DashboardMain.vue'),
sidebar: () => import('../views/DashboardSidebar.vue')
}
}
整体修改src/router/index.ts如下:
TypeScript
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
const routes: RouteRecordRaw[] = [
// 基础路由
{
path: '/',
name: 'home',
component: () => import('../views/HomePage.vue')
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutPage.vue')
},
// 动态路由(承接上一节)
{
path: '/todo/:id',
name: 'todo-detail',
component: () => import('../views/TodoDetail.vue')
},
// ========== 4.1 嵌套路由 ==========
{
path: '/user',
name: 'user',
component: () => import('../views/UserLayout.vue'),
// 子路由数组
children: [
// 默认子路由:访问 /user 时渲染
{
path: '',
name: 'user-profile',
component: () => import('../views/UserProfile.vue')
},
// 子路由:访问 /user/settings
{
path: 'settings',
name: 'user-settings',
component: () => import('../views/UserSettings.vue')
}
]
},
// ========== 4.2 命名视图 ==========
{
path: '/dashboard',
name: 'dashboard',
// 命名视图使用 components (复数)
components: {
default: () => import('../views/DashboardMain.vue'),
sidebar: () => import('../views/DashboardSidebar.vue')
}
},
// 404 兜底路由
{
path: '/:pathMatch(.*)*',
name: 'not-found',
component: () => import('../views/NotFound.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
4.3 公共布局 src/views/UserLayout.vue
TypeScript
<template>
<div class="user-layout">
<!-- 侧边导航 -->
<aside class="aside">
<h3>用户中心</h3>
<div class="nav-item">
<router-link to="/user">个人资料</router-link>
</div>
<div class="nav-item">
<router-link to="/user/settings">账户设置</router-link>
</div>
</aside>
<!-- 子页面渲染出口 -->
<main class="main">
<router-view />
</main>
</div>
</template>
<style scoped>
.user-layout {
display: flex;
gap: 20px;
margin-top: 20px;
}
.aside {
width: 180px;
padding: 16px;
background: #f5f5f5;
}
.nav-item {
margin: 10px 0;
}
a {
text-decoration: none;
color: #333;
}
a.router-link-active {
color: #42b983;
font-weight: bold;
}
.main {
flex: 1;
padding: 16px;
border: 1px solid #eee;
}
</style>
4.4 默认子页面 src/views/UserProfile.vue
TypeScript
<template>
<div>
<h2>个人资料</h2>
<p>这是 /user 路由对应的默认子页面</p>
</div>
</template>
4.5 设置页面 src/views/UserSettings.vue
TypeScript
<template>
<div>
<h2>账户设置</h2>
<p>这是 /user/settings 子页面</p>
</div>
</template>
4.6 侧边栏视图 src/views/DashboardSidebar.vue
TypeScript
<template>
<div class="sidebar">
<h3>控制台侧边栏</h3>
<p>菜单1</p>
<p>菜单2</p>
<p>菜单3</p>
</div>
</template>
<style scoped>
.sidebar {
width: 160px;
padding: 16px;
background: #eef2f7;
}
</style>
4.7 主内容视图 src/views/DashboardMain.vue
TypeScript
<template>
<div class="main-content">
<h2>控制台主内容</h2>
<p>命名视图 - 默认视图区域</p>
</div>
</template>
<style scoped>
.main-content {
flex: 1;
padding: 16px;
border: 1px solid #ccc;
}
</style>
4.8 修改根组件 App.vue
TypeScript
<template>
<div class="app">
<!-- 全局导航 -->
<nav class="global-nav">
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
<router-link to="/todo/888">动态路由</router-link>
<router-link to="/user">用户中心(嵌套路由)</router-link>
<router-link to="/dashboard">控制台(命名视图)</router-link>
</nav>
<hr>
<!-- 命名视图容器:两个带 name 的 router-view -->
<div class="dashboard-box">
<router-view name="sidebar" />
<router-view /> <!-- 不写name,对应 default 视图 -->
</div>
</div>
</template>
<style scoped>
.global-nav {
margin: 10px 0;
}
.global-nav a {
margin-right: 15px;
text-decoration: none;
}
.global-nav a.router-link-active {
color: #42b983;
font-weight: bold;
}
.dashboard-box {
display: flex;
gap: 20px;
margin-top: 20px;
}
</style>



五、导航方式与路由传参
5.1 声明式导航:<router-link>
它比硬编码的 <a> 更智能,会自动添加激活时的 CSS 类 router-link-active 和 router-link-exact-active,方便高亮当前导航。
TypeScript
<router-link to="/" exact-active-class="exact-active">首页</router-link>
5.2 编程式导航
在 JavaScript 中跳转:
TypeScript
import { useRouter } from 'vue-router'
const router = useRouter()
// 字符串
router.push('/about')
// 命名路由 + params
router.push({ name: 'todo-detail', params: { id: 123 } })
// 带 query
router.push({ path: '/search', query: { q: 'vue' } })
push 会添加一条历史记录,replace 则会替换当前记录(不会留下记录)。还有 router.go(n) 可以前进或后退 n 步。
5.3 路由传参的方式与选择
除了 params,还可以使用 query。它们的区别:
-
params属于路径的一部分,在动态路由中定义(:id),刷新页面不会丢失。 -
query是?后面的键值对,不需要预先在路由声明中配置,通常用于可选筛选条件。
TypeScript 提示 :当使用命名路由跳转时,Vue Router 4 提供了有限的类型推断。可以借助 unplugin-vue-router 等增强类型,但对于本篇学习,强类型断言已经足够。
六、路由守卫:把控跳转的每一关
导航守卫让你能在路由切换的各个阶段拦截或重定向。它就像一道安检门,你可以定义"谁可以进、谁需要先登录"。
6.1 全局守卫
在 router/index.ts 中定义,作用于所有路由:
TypeScript
const router = createRouter({ ... })
// 全局前置守卫
router.beforeEach((to, from, next) => {
// 根据条件放行或跳转
if (to.meta.requiresAuth && !isLoggedIn()) {
next({ name: 'login', query: { redirect: to.fullPath } })
} else {
next()
}
})
// 全局解析守卫(导航被确认之前,所有组件内守卫和异步路由组件被解析之后)
router.beforeResolve((to, from, next) => {
// 可用于加载数据
next()
})
// 全局后置钩子(不会改变导航本身)
router.afterEach((to, from) => {
document.title = to.meta.title as string || '我的应用'
})
Vue Router 4 的守卫支持返回一个值或 Promise 来取代 next 回调(但仍支持 next):
TypeScript
router.beforeEach(async (to, from) => {
if (to.meta.requiresAuth && !(await checkAuth())) {
return { name: 'login' }
}
})
6.2 路由独享守卫
可以直接在路由记录上定义 beforeEnter:
TypeScript
const routes = [
{
path: '/admin',
component: AdminPage,
beforeEnter: (to, from) => {
// 只在进入此路由时触发
if (!isAdmin()) return { name: 'forbidden' }
}
}
]
6.3 组件内守卫
在组件内部,你可以通过 onBeforeRouteUpdate 和 onBeforeRouteLeave 钩子实现组件级守卫:
TypeScript
<script setup lang="ts">
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
onBeforeRouteUpdate((to, from) => {
// 当前路由改变但组件复用时调用(如 /todo/1 → /todo/2)
if (hasUnsavedChanges.value) {
const answer = window.confirm('有未保存更改,确定离开吗?')
if (!answer) return false
}
})
onBeforeRouteLeave((to, from) => {
// 导航离开时调用
window.clearInterval(timer) // 清理
})
</script>
6.4 完整的导航解析流程
了解生命周期有助于调试:

编辑
七、路由元信息与类型扩展
7.1 使用 meta 存储附加信息
每个路由都可以携带 meta 对象,用于存储权限、图标、标题等自定义数据:
TypeScript
const routes: RouteRecordRaw[] = [
{
path: '/admin',
component: AdminPage,
meta: { requiresAuth: true, role: 'admin', title: '管理后台' }
}
]
在守卫中读取:
TypeScript
router.beforeEach((to) => {
if (to.meta.requiresAuth) { ... }
})
7.2 为 meta 扩展 TypeScript 类型
默认情况下 route.meta 是 any 类型。为了获得智能提示,可以扩展 RouteMeta 接口:
TypeScript
// src/types/router.d.ts
import 'vue-router'
declare module 'vue-router' {
interface RouteMeta {
requiresAuth?: boolean
title?: string
role?: string
}
}
现在在守卫或组件中访问 to.meta.title,TypeScript 会知道你声明的所有字段,极大提升开发体验。
八、综合案例:Todo 应用的多页面改造
让我们把第二篇和第三篇的 Todo 应用正式进化为 SPA,拥有三个页面:列表页、详情页、设置页。
TypeScript
src/
├── router
│ └── index.ts # 路由配置
├── types.ts # TS类型定义
├── App.vue # 根布局+导航+路由出口
├── views
│ ├── TodoListPage.vue # 待办列表页
│ ├── TodoDetailPage.vue# 单条详情页
│ └── SettingsPage.vue # 设置页面
└── components
├── TodoHeader.vue
├── TodoInput.vue
├── TodoList.vue
├── TodoItem.vue
└── TodoFooter.vue
8.1 路由表设计
src/router/index.ts
TypeScript
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'todo-list',
component: () => import('../views/TodoListPage.vue'),
meta: { title: '待办列表' }
},
{
path: '/todo/:id',
name: 'todo-detail',
component: () => import('../views/TodoDetailPage.vue'),
meta: { title: '待办详情' }
},
{
path: '/settings',
name: 'settings',
component: () => import('../views/SettingsPage.vue'),
meta: { title: '系统设置' }
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
// 全局后置钩子修改浏览器标题
router.afterEach((to) => {
document.title = (to.meta.title as string) || 'Vue3 Todo SPA应用'
})
export default router
8.2 列表页:TodoListPage.vue
我们将原先的 App.vue 中的主要逻辑搬迁到列表页,保留 TodoHeader、TodoInput、TodoList 和 TodoFooter 组件。同时增加点击项目跳转到详情页的功能。
TypeScript
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import TodoHeader from '../components/TodoHeader.vue'
import TodoInput from '../components/TodoInput.vue'
import TodoList from '../components/TodoList.vue'
import TodoItem from '../components/TodoItem.vue'
import TodoFooter from '../components/TodoFooter.vue'
import type { Todo } from '../types'
const router = useRouter()
const todos = ref<Todo[]>([])
let nextId = 1
const activeCount = computed(() => todos.value.filter(t => !t.done).length)
const allDone = computed({
get: () => todos.value.length > 0 && activeCount.value === 0,
set: (val: boolean) => todos.value.forEach(t => t.done = val)
})
function addTodo(text: string) {
todos.value.push({ id: nextId++, text, done: false })
}
function toggleTodo(id: number) {
const todo = todos.value.find(t => t.id === id)
if (todo) todo.done = !todo.done
}
function removeTodo(id: number) {
todos.value = todos.value.filter(t => t.id !== id)
}
function clearCompleted() {
todos.value = todos.value.filter(t => !t.done)
}
function goToDetail(id: number) {
router.push({ name: 'todo-detail', params: { id } })
}
onMounted(() => {
const saved = localStorage.getItem('vue3-todos')
if (saved) {
try {
const data = JSON.parse(saved) as Todo[]
todos.value = data
nextId = data.reduce((max, t) => Math.max(max, t.id), 0) + 1
} catch {}
}
})
</script>
<template>
<div class="todo-page">
<TodoHeader :active-count="activeCount" :total="todos.length" />
<TodoInput @add="addTodo" />
<TodoList :todos="todos" @toggle="toggleTodo" @remove="removeTodo">
<template #item="{ todo }">
<TodoItem :todo="todo" @toggle="toggleTodo" @remove="removeTodo"
@click="goToDetail(todo.id)" />
</template>
</TodoList>
<TodoFooter v-if="todos.length > 0" v-model="allDone" @clear-completed="clearCompleted" />
</div>
</template>
注意我们给 TodoItem 添加了点击事件,跳转到详情页。
8.3 详情页:TodoDetailPage.vue
从路由获取 ID,并读取数据展示。真实项目中会从 API 或状态管理获取,这里简单从 localStorage 查找。
TypeScript
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import type { Todo } from '../types'
const route = useRoute()
const router = useRouter()
const todo = ref<Todo | null>(null)
onMounted(() => {
const id = Number(route.params.id)
const saved = localStorage.getItem('vue3-todos')
if (saved) {
const todos = JSON.parse(saved) as Todo[]
todo.value = todos.find(t => t.id === id) || null
}
})
function goBack() {
router.push({ name: 'todo-list' })
}
</script>
<template>
<div>
<button @click="goBack">← 返回列表</button>
<div v-if="todo">
<h1>{{ todo.text }}</h1>
<p>状态:{{ todo.done ? '已完成' : '未完成' }}</p>
<p>ID:{{ todo.id }}</p>
</div>
<div v-else>
<p>待办不存在</p>
</div>
</div>
</template>
8.4 导航栏 App.vue
App.vue 现在只需提供公共导航和路由出口:
<template>
<div id="app">
<nav class="app-nav">
<router-link to="/">列表</router-link>
<router-link to="/settings">设置</router-link>
</nav>
<main class="app-main">
<router-view />
</main>
</div>
</template>
<style scoped>
.app-nav {
padding: 12px;
background: #f0f0f0;
display: flex;
gap: 16px;
}
.router-link-active {
color: #42b883;
font-weight: bold;
}
</style>
现在运行应用,你可以在列表页、详情页和设置页之间无缝切换,浏览器前进后退按钮完美工作,URL 也始终同步。
此处插入图片:多页面Todo应用截图拼图,展示列表页、详情页和设置页
九、本篇总结
本文我们系统学习了 Vue Router,从前端路由的原理到环境配置,再到动态路由、嵌套路由、编程导航、路由守卫和元信息。通过重构 Todo 应用,你将组件化与路由结合起来,实现了真正意义上的单页应用。
关键收获:
-
前端路由让 SPA 的 URL 变化可控,主要有 Hash 和 History 两种模式。
-
createRouter+createWebHashHistory初始化路由,<router-view>是出口,<router-link>声明导航。 -
动态路由用
:param定义,useRoute().params获取参数,复用组件时需watch参数变化。 -
嵌套路由通过
children实现布局复用,命名视图可渲染多个同级组件。 -
router.push/replace/go进行编程式导航,params和query各有适用场景。 -
路由守卫体系(全局、独享、组件内)让你能够拦截导航,实现权限控制、数据预载等逻辑。
-
利用
meta和 TypeScript 扩展增强代码安全性和可维护性。
下篇预告 : 有了路由,我们还缺一个能全局共享状态、跨组件通信的利器------状态管理。下一篇将走进 Pinia,Vue 3 的官方状态管理库。你将会学到如何定义 Store、使用 State/Getters/Actions,在 Todo 应用中实现数据持久化与多页面共享,以及与 Vue Devtools 的结合,让复杂应用的状态管理变得清晰而优雅。
总结 路由是 SPA 的骨架,它让应用从单一的页面变成了有机的整体。本文从原理到实战,覆盖了 Vue Router 在实际开发中最常用的知识和技巧。请务必跟着教程亲手重构 Todo 应用,当你点击"详情"看到 URL 变化、内容切换,却没有任何刷新时,就能真正理解前端路由的魅力。掌握路由,你就拥有了构建大型前端应用的导航能力。
此处插入图片:Vue Router 知识体系思维导图,涵盖路由配置、导航守卫、组合式 API 等