Vue.js: Vue Router 路由
最后更新:2026-10-20
Vue Router 是 Vue 官方的路由管理器——让你的 SPA(单页应用)有多个"页面",每个 URL 对应一个组件。Vue Router 4 是 Vue 3 配套版本,完全支持 Composition API。
SPA 路由的核心是URL ↔ 组件的映射。掌握 Vue Router 4 是开发任何非 trivial Vue 应用的必备技能。
1. 你将学到
- createRouter + createWebHistory 初始化
- 5 种路由:静态、动态、嵌套、命名、通配
- 编程式导航(router.push / replace / go)
- 路由懒加载(动态 import)
- 路由参数(params / query)
- 路由守卫(beforeEach / beforeEnter)
- 5 大实战场景
2. 一个 SPA 的"404 噩梦"
(1) 痛点:5 个页面,5 份重复代码
Alice 的后台有 5 个页面(Home/Products/Orders/Users/Settings):
4 个问题:
- URL 不变:刷新页面丢失当前页面
- 无法分享链接:不能复制 URL 给同事
- 无浏览器前进/后退:用户体验差
- SEO 差:搜索引擎看不懂
(2) Vue Router 4 解法:5 行路由 + 5 个组件
JS
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{ path: '/', component: () => import('@/views/HomePage.vue') },
{ path: '/products', component: () => import('@/views/ProductsPage.vue') },
{ path: '/orders', component: () => import('@/views/OrdersPage.vue') },
{ path: '/users', component: () => import('@/views/UsersPage.vue') },
{ path: '/settings', component: () => import('@/views/SettingsPage.vue') }
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
5 行路由 + 1 个 router-view = 完整 SPA。URL 自动同步,浏览器前进/后退工作。
(3) 收益
使用 Vue Router 后:
- URL 同步:自动同步,刷新保留
- 可分享:任何页面都有 URL
- 浏览器历史:前进/后退正常工作
- SEO 友好:SSR 支持(Nuxt 3)
- 代码量:5 个 v-if → 5 行 routes 数组(-80%)
3. 路由初始化
(1) 完整初始化代码
JS
// src/router/index.js
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
// 1. 定义路由(懒加载)
const routes = [
{ path: '/', component: () => import('@/views/HomePage.vue') },
{ path: '/products', component: () => import('@/views/ProductsPage.vue') }
]
// 2. 创建 router 实例
const router = createRouter({
// history 模式:URL 无 #(推荐)
history: createWebHistory(),
// hash 模式:URL 带 #(兼容老浏览器)
// history: createWebHashHistory(),
routes,
// 滚动行为:路由切换时滚到顶部
scrollBehavior(to, from, savedPosition) {
return savedPosition || { top: 0 }
}
})
export default router
(2) main.js 注册
(3) 2 大 history 模式对比
| 维度 | createWebHistory | createWebHashHistory |
|---|---|---|
| URL 形式 | /products |
/#/products |
| SEO | ✅ 友好 | ❌ 不友好 |
| 服务器配置 | 需要(try_files) | 不需要 |
| 浏览器兼容 | IE10+ | IE8+ |
| 推荐度 | ⭐⭐⭐⭐⭐ | ⭐⭐ |
4. 5 种路由配置
(1) 静态路由
JS
const routes = [
{ path: '/', component: () => import('@/views/HomePage.vue') },
{ path: '/about', component: () => import('@/views/AboutPage.vue') },
{ path: '/contact', component: () => import('@/views/ContactPage.vue') }
]
(2) 动态路由(参数)
JS
const routes = [
// /products/123
{
path: '/products/:id',
component: () => import('@/views/ProductDetailPage.vue')
},
// /users/456/posts/789
{
path: '/users/:userId/posts/:postId',
component: () => import('@/views/PostPage.vue')
},
// 可选参数
{
path: '/news/:category?',
component: () => import('@/views/NewsPage.vue')
}
]
VUE
<!-- ProductDetailPage.vue 接收参数 -->
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.params.id) // '123'
</script>
(3) 嵌套路由
JS
const routes = [
{
path: '/users',
component: () => import('@/views/UsersPage.vue'),
children: [
{
path: '', // 默认子路由
component: () => import('@/views/UsersListPage.vue')
},
{
path: ':id', // /users/123
component: () => import('@/views/UserDetailPage.vue')
}
]
}
]
(4) 命名路由
JS
const routes = [
{
path: '/products/:id',
name: 'product-detail', // 命名路由
component: () => import('@/views/ProductDetailPage.vue')
}
]
VUE
<!-- 通过 name 跳转,避免硬编码 URL -->
<router-link :to="{ name: 'product-detail', params: { id: 123 } }">
Product 123
</router-link>
(5) 通配符 404
JS
const routes = [
{ path: '/', component: () => import('@/views/HomePage.vue') },
// ... 其他路由
{
path: '/:pathMatch(.*)*', // 匹配所有未定义的路由
component: () => import('@/views/NotFoundPage.vue')
}
]
▶ 示例:手写 Hash 路由器(CDN 模拟 SPA 路由)
▶ 展开运行示例(86 行)
输出:
TEXT
初始地址为 #/ 时 Home 链接高亮,内容区显示「🏠 Home / 这是首页」,底部显示「当前路由: / | 历史栈: 1 步」,「← 后退」按钮置灰。
点击 About 后内容变为「ℹ️ About / 关于我们」,历史栈变为 2 步,「← 后退」可用。
点击 User 123 显示「👤 User 123 / 用户资料页」;点击 Products 显示「📦 Products / 商品列表页…」。
点「← 后退」回到上一页,内容区与「当前路由」同步更新,历史栈只剩 1 步时后退按钮再次置灰。
⚠️ 此为教学演示 Hash 路由原理。生产项目用
vue-router 官方库(必须 Vite)。
(1) 📊 5 种导航方式速查
| 方式 | 代码 | 适用 |
|---|---|---|
| 声明式链接 | <router-link to="/products"> |
用户点击链接 |
| 字符串路径 | router.push('/products') |
JS 跳转 |
| 命名路由 | router.push({ name: 'products' }) |
推荐(重命名不破坏) |
| 动态参数 | router.push({ name: 'user', params: { id: 123 } }) |
详情页 |
| Query 参数 | router.push({ path: '/search', query: { q: 'vue' } }) |
搜索/过滤 |
5. 5 种导航方式
(1) 声明式导航(router-link)
(2) 编程式导航(router.push)
JS
import { useRouter } from 'vue-router'
const router = useRouter()
function goToProduct(id) {
router.push(`/products/${id}`) // 字符串
// 或
router.push({ name: 'product-detail', params: { id } }) // 对象
}
function goBack() {
router.back() // 后退
}
function goForward() {
router.forward() // 前进
}
function replaceCurrent() {
router.replace('/login') // 替换(不留历史)
}
(3) 5 种 push vs replace 区别
| 方式 | 行为 | 历史记录 |
|---|---|---|
router.push() |
跳转到新页面 | 留下记录(可后退) |
router.replace() |
替换当前页面 | 不留记录(不可后退) |
router.go(n) |
前进/后退 n 步 | 走历史 |
router.back() |
后退一步 | 走历史 |
router.forward() |
前进一步 | 走历史 |
(4) 编程式 vs 声明式对比
| 场景 | 推荐 |
|---|---|
| 用户点击链接 | router-link(声明式) |
| JS 触发跳转 | router.push(编程式) |
| 表单提交后跳转 | router.push |
| 登录后跳转 | router.replace |
| 404 自动回退 | router.back |
▶ 示例:hash 迷你路由(push / replace / back 的差别)
▶ 试一试浏览器里跑不起真正的 vue-router(需要构建),这里用
location.hash+hashchange手写一个迷你路由,专门演示编程式导航三种写法的历史记录差异。
输出:
TEXT
初始打开(地址栏没有 hash)时路径显示 /,当前页面「首页 Home」,hash 变化次数 0。
点 push('/products'):地址栏变 #/products,页面变「商品列表 Products」,次数 1。
点 replace('/search'):地址变 #/search,页面变「搜索结果 Search」,次数 2。
此时点 back()(或浏览器后退):直接回到初始的首页 /,#/products 那条记录已被 replace 覆盖——replace 不留历史。
6. 路由守卫(导航守卫)
(1) 3 种守卫类型
JS
// 1. 全局前置守卫(最常用)
router.beforeEach((to, from, next) => {
const isLoggedIn = !!localStorage.getItem('token')
if (to.meta.requiresAuth && !isLoggedIn) {
next('/login') // 跳登录
} else {
next() // 放行
}
})
// 2. 路由独享守卫
const routes = [
{
path: '/admin',
component: () => import('@/views/AdminPage.vue'),
beforeEnter: (to, from, next) => {
if (isAdmin()) next()
else next('/403')
}
}
]
// 3. 组件内守卫
// 组件内:beforeRouteEnter / beforeRouteUpdate / beforeRouteLeave
(2) 完整登录守卫示例
JS
// router/index.js
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token')
const isLoggedIn = !!token
// 需要登录的页面
if (to.meta.requiresAuth && !isLoggedIn) {
return next({
name: 'login',
query: { redirect: to.fullPath } // 登录后跳回
})
}
// 已登录访问登录页
if (to.name === 'login' && isLoggedIn) {
return next({ name: 'home' })
}
next()
})
JS
// 路由 meta 配置
const routes = [
{
path: '/dashboard',
component: () => import('@/views/DashboardPage.vue'),
meta: { requiresAuth: true } // 需要登录
}
]
7. 综合演练:电商后台登录守卫
▶ 示例:完整登录守卫(⚠️ 需 Vite 项目)
JS
// router/index.js
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token')
const isLoggedIn = !!token
if (to.meta.requiresAuth && !isLoggedIn) {
return next({ name: 'login', query: { redirect: to.fullPath } })
}
if (to.name === 'login' && isLoggedIn) {
return next({ name: 'home' })
}
next()
})
// 路由 meta 配置
const routes = [
{
path: '/dashboard',
component: () => import('@/views/DashboardPage.vue'),
meta: { requiresAuth: true } // 需要登录
}
]
输出:
TEXT
未登录访问 /dashboard 时被重定向到 /login?redirect=/dashboard,地址栏保留原目标路径。
已登录再访问 /login 会直接跳到首页,不会停留在登录页。
访问公开页或已登录访问受保护页时守卫调用 next(),目标页面正常渲染。
(2) 📊 5 大常见错误速查
| 错误 | 现象 | 解决 |
|---|---|---|
| router-view 缺失 | 页面不显示 | 添加 <router-view /> |
| 路由没懒加载 | 首屏慢 | 用 () => import() 动态导入 |
| 守卫死循环 | 跳不出 | 避免 next() 又触发 beforeEach |
| 嵌套路由无 outlet | 子页面不显示 | 父组件加 <router-view /> |
| 404 路由缺失 | 报错 | 加 path: '/:pathMatch(.*)*' |
(3) 📊 5 大性能对比
| 模式 | 首屏 | 切换 | 适用 |
|---|---|---|---|
| 静态 import | 慢 | 快 | 小项目 |
| 动态 import | ⭐⭐⭐⭐⭐ | 首次慢 | 推荐 |
| 路由分组懒加载 | ⭐⭐⭐⭐⭐ | 首次慢 | 大项目 |
| prefetch | ⭐⭐⭐⭐ | 快 | 用户体验优先 |
| SSR | ⭐⭐⭐⭐⭐ | 快 | Nuxt 3 |
❓ 常见问题
Q createWebHistory 和 createWebHashHistory 选哪个?
A createWebHistory(URL 无 #,SEO 友好)需要服务器配置;createWebHashHistory(URL 带 #)兼容老浏览器。生产环境推荐 createWebHistory。
Q 动态路由怎么接收参数?
A 用
useRoute() 钩子:const route = useRoute(); route.params.id。Q router.push 和 router.replace 区别?
A push 留历史记录(可后退),replace 替换当前(不留历史)。登录后用 replace(不能后退回登录页)。
Q 路由守卫能异步吗?
A 能。
beforeEach(async (to, from, next) => { await checkAuth(); next() })。但要小心避免死循环。Q 嵌套路由怎么实现?
A 父路由用
children: [] 配置子路由,父组件模板中加 <router-view /> 作为子路由出口。Q Vue Router 4 和 Vue Router 3 区别?
A Vue Router 4 支持 Composition API(useRouter/useRoute),TypeScript 推断更强,导航守卫参数变了(to/from/next)。Vue 3 项目必须用 Vue Router 4。
Q 路由懒加载能提升多少性能?
A 首屏可减少 50-80% 加载时间(不加载未访问的页面)。大项目(10+ 页面)效果显著。
Q 路由守卫 meta 字段怎么用?
A 路由配置加
meta: { requiresAuth: true },守卫中用 to.meta.requiresAuth 访问。用于权限、标题、面包屑等。📖 小节
- Vue Router 4 是 Vue 3 官方路由管理器
- 5 种路由:静态、动态、嵌套、命名、通配符
- 5 种导航:router-link、router.push、replace、back、forward
- 3 种守卫:全局(beforeEach)、路由独享(beforeEnter)、组件内
- 路由懒加载(动态 import)减少首屏
- createWebHistory 推荐,createWebHashHistory 兼容老浏览器
- 嵌套路由用 children +
<router-view />
📝 作业
-
基础题(难度⭐) 实现一个简单的多页 SPA:
- 3 个页面:Home / About / Contact
- 顶部导航 + 底部 router-view
- 用
vue-router配置路由
-
进阶题(难度⭐⭐) 实现电商商品路由:
/products列表页/products/:id详情页(动态参数)/products/:id/edit编辑页- 404 处理
-
挑战题(难度⭐⭐⭐) 实现完整的电商后台路由系统:
- 10+ 页面(home/products/orders/users/settings/login/404 等)
- 嵌套路由(/admin/dashboard, /admin/products 等)
- 路由懒加载(首屏快)
- 登录守卫(未登录跳 /login,登录后跳回)
- 5 种导航方式全覆盖
- 编程式导航(点击按钮跳转)
- TypeScript 强类型 routes 配置