引言
在现代 Web 开发中,单页应用(SPA)已成为主流架构之一,而路由则是 SPA 实现页面切换与状态管理的核心机制。
对于 Vue3 开发者而言,Vue Router 作为官方配套的路由解决方案,不仅能无缝衔接 Vue3 的组合式 API,还能提供灵活的导航控制、路由守卫等功能,是构建复杂应用的必备工具。
本文将从路由原理入手,逐步讲解 Vue3 路由的定义方式与实际实现,帮助开发者从 “会用” 到 “懂原理”,高效解决路由开发中的常见问题。
Vue3 路由基础概念
什么是路由
路由的本质是 “URL 与资源的映射关系”。在传统多页应用中,URL 变化会触发页面刷新,服务器会返回对应页面的 HTML 资源;
而在 Vue3 单页应用中,路由通过 “无刷新” 的方式实现 URL 变化与组件渲染的关联 —— 即 URL 改变时,页面不重新加载,仅切换当前渲染的 Vue 组件,从而提升用户体验。
Vue Router 简介
Vue Router
是 Vue 官方推出的路由管理库,专门用于 Vue3 项目的路由控制,其核心功能包括:
- 建立 URL 与 Vue 组件的映射关系;
- 支持 “无刷新” 页面跳转(导航);
- 提供动态路由、嵌套路由、路由懒加载等高级特性;
- 通过 “导航守卫” 控制路由跳转的权限(如登录校验);
- 支持 Hash、History 两种路由模式,适配不同部署场景。
Vue3 路由原理剖析
Vue Router 的核心原理是 “监听 URL 变化,匹配对应的路由规则,渲染目标组件”,其关键机制包括路由模式与导航守卫。
路由模式
Vue Router 支持三种路由模式,不同模式的核心区别在于 “如何监听 URL 变化” 与 “URL 的表现形式”,其中 Hash 和 History 模式最为常用。
Hash 模式
- 原理:基于浏览器的
location.hash 属性实现。location.hash 指向 URL 中 # 后面的部分(如 https://xxx.com/#/home 中的 /home),
该部分的变化不会触发浏览器向服务器发送请求;Vue Router 通过监听 window 的 hashchange 事件,感知 hash 值的变化,进行匹配对应的路由规则。
- URL 特点:包含
# 符号,如 https://my-blog.com/#/article/123 。
- 优缺点:
- 优点:兼容性好,无需服务器端配置。
- 缺点:URL 中包含
# 符号,不美观;hashchange 事件触发时,会导致浏览器滚动到顶部,影响用户体验。
History 模式
- 原理:基于 HTML5 History API 实现。History API 提供了
pushState 和 replaceState 方法,
这两个方法可以在不触发页面刷新的前提下,修改浏览器的历史记录与 URL;Vue Router 通过调用这两个方法改变 URL,并监听 window 的 popstate 事件(用户点击浏览器前进 / 后退按钮时触发),从而感知 URL 变化。
- URL 特点:不包含
# 符号,如 https://my-blog.com/article/123 。
- 优缺点:
- 优点:URL 美观,符合 RESTful 风格;不会触发页面刷新,用户体验好。
- 缺点:兼容性差(仅支持 IE10+ 及现代浏览器,需要服务器端配置支持,否则刷新页面时,服务器会因找不到对应 URL 而返回 404(如 Apache 需开启
mod_rewrite 模块)。
- 后端配置要求:
- Apache:需开启
mod_rewrite 模块,配置 RewriteEngine On 与 RewriteBase /。
- Nginx:需配置
location / { try_files $uri $uri/ /index.html; }。
1
2
3
4
5
|
location / {
root /path/to/your/vue/app;
index index.html;
try_files $uri $uri/ /index.html; # 关键配置:将所有请求转发到 index.html
}
|
- Node.js:可使用
connect-history-api-fallback 中间件。
Memory 模式
- 原理:不依赖浏览器 URL,而是将路由状态存储在内存中(如,一个数组),仅在应用内部维护路由变化。
- 适用场景:非浏览器环境,(如 Electron 桌面应用、Vue3 小程序),或不需要 URL 与路由关联的场景。
- 特点:URL 不会随路由变化,刷新页面后路由状态会丢失。
导航守卫机制
导航守卫是 Vue Router 提供的 “路由跳转钩子”,用于在路由跳转的不同阶段执行自定义逻辑(如权限校验、页面埋点),其本质是 “拦截路由跳转,根据条件决定是否允许跳转”。
根据作用范围,导航守卫分为三类:
全局守卫
作用于所有路由,需在路由实例创建时配置,常用守卫包括:
beforeEach:路由跳转前触发(全局前置守卫),可用于登录校验;
afterEach:路由跳转后触发(全局后置守卫),可用于页面标题修改、埋点统计;
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory(),
routes: [...], // 路由规则
});
// 全局前置守卫:登录校验
router.beforeEach((to, from, next) => {
// to:目标路由;from:当前路由;next:继续跳转的函数
const isLogin = localStorage.getItem('token'); // 假设token存储登录状态
// 若未登录且目标路由需要登录,则跳转至登录页
if (!isLogin && to.meta.requiresAuth) {
next('/login');
} else {
next(); // 允许跳转
}
});
// 全局后置守卫:修改页面标题
router.afterEach((to) => {
document.title = to.meta.title || 'Vue3 App'; // 从路由元信息中获取标题
});
|
路由独享守卫
仅作用于指定路由,在路由规则中配置,常用 beforeEnter (路由进入前触发)。
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
const routes = [
{
path: '/article/:id', // 动态路由(:id 为参数)
component: () => import('@/views/Article.vue'), // 懒加载组件
meta: { title: '文章详情' },
// 路由独享守卫:校验文章 ID 格式
beforeEnter: (to, from, next) => {
const articleId = to.params.id;
// 若 ID 不是数字,则跳转至 404 页面
if (!/^\d+$/.test(articleId)) {
next('/404');
} else {
next();
}
}
}
];
|
组件内守卫
作用于当前组件,在组件内部通过组合式 API 定义。常用守卫包括:
onBeforeRouteEnter:进入组件前触发(此时组件实例未创建,无法访问 this)
onBeforeRouteUpdate:组件复用但路由参数变化时触发(如从 /article/1 跳转至 /article/2);
onBeforeRouteLeave:离开组件前触发(可用于提示 未保存的内容是否丢弃)
示例代码(组合式 API):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<template>
<div>文章详情:{{ articleId }}</div>
</template>
<script setup>
import { onBeforeRouteEnter, onBeforeRouteLeave, useRoute } from 'vue-router';
const route = useRoute();
const articleId = route.params.id; // 获取路由参数
// 进入组件前触发
onBeforeRouteEnter((to, from, next) => {
// 可通过next(vm => { ... })访问组件实例vm
next(vm => {
console.log('进入组件,文章ID:', vm.articleId);
});
});
// 离开组件前触发
onBeforeRouteLeave((to, from, next) => {
const confirmLeave = window.confirm('内容未保存,是否离开?');
confirmLeave ? next() : next(false); // 确认则离开,否则取消跳转
});
</script>
|
Vue3 路由的定义与配置
Vue3 路由的配置需遵循 “安装依赖 → 创建路由实例 → 定义路由规则 → 注入 Vue 应用” 的流程,以下是详细步骤。
安装 Vue Router
首先在 Vue3 项目中安装 Vue Router (需使用 4.x 版本,适配 Vue3),命令如下:
- 使用 npm:
1
|
npm install vue-router@4 --save
|
- 使用 yarn:
创建路由实例
在项目中新建路由配置文件(通常为 src/router/index.js),通过 createRouter 和 createWebHistory (或 createWebHashHistory) 创建路由实例:
示例代码:
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
|
import { createRouter, createWebHistory } from 'vue-router';
// 引入组件(也可使用懒加载,见4.5节)
import Home from '@/views/Home.vue';
import Login from '@/views/Login.vue';
import Article from '@/views/Article.vue';
import NotFound from '@/views/NotFound.vue';
// 1. 定义路由规则
const routes = [
{ path: '/', redirect: '/home' }, // 重定向:默认跳转至/home
{
path: '/home',
component: Home,
meta: { title: '首页', requiresAuth: false } // meta:路由元信息(自定义)
},
{ path: '/login', component: Login, meta: { title: '登录' } },
{ path: '/article/:id', component: Article, meta: { title: '文章详情' } },
{ path: '/:pathMatch(.*)*', component: NotFound, meta: { title: '404' } } // 404路由
];
// 2. 创建路由实例
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), // History模式(Vue3 Vite项目需加BASE_URL)
// history: createWebHashHistory(), // 若使用Hash模式,替换上方代码
routes // 传入路由规则
});
// 3. 导出路由实例(供main.js注入)
export default router;
|
注入 Vue 应用
在 Vue3 入口文件 (src/main.js) 中,通过 app.use(router) 将路由实例注入 Vue 应用,确保路由功能全局可用:
示例代码:
1
2
3
4
5
6
7
8
|
import { createApp } from 'vue';
import App from './App.vue';
import router from './router'; // 引入路由实例
import './style.css';
const app = createApp(App);
app.use(router); // 注入路由
app.mount('#app');
|
定义路由规则
路由规则是 routes 数组中的每一项,核心属性包括:
path:URL 路径(如 /home、/article/:id)
component:路径对应的 Vue 组件
meta:路由元信息(自定义属性,如 title、requiresAuth)
redirect:重定向(如 { path: '/', redirect: '/home' })
children:嵌套路由规则(见 嵌套路由
)
动态路由定义
当 URL 路径中包括 “可变参数” 时(如文章 ID、用户 ID),需使用动态路由,格式为 path: '/xxx/:参数名,参数可通过 useRoute().params 获取。
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
const routes = [
{
path: '/user/:username', // :username为动态参数
component: () => import('@/views/User.vue'),
meta: { title: '用户主页' }
}
];
// 在User组件中获取参数(组合式API)
<script setup>
import { useRoute } from 'vue-router';
const route = useRoute();
console.log('用户名:', route.params.username); // 如URL为/user/zhangsan,则输出zhangsan
</script>
|
嵌套路由
当组件需要包含 “子组件” (如 “首页” 包含 “推荐”、“热门” 等子模块)时,需要使用嵌套路由,核心是通过 children 属性定义子路由,并在父组件中使用
<router-view> 渲染子组件。
示例步骤:
- 定义嵌套路由规则:
1
2
3
4
5
6
7
8
9
10
11
|
const routes = [
{
path: '/home',
component: Home, // 父组件
children: [
{ path: '', redirect: 'recommend' }, // 子路由默认重定向
{ path: 'recommend', component: HomeRecommend, meta: { title: '首页-推荐' } }, // 子路由1
{ path: 'hot', component: HomeHot, meta: { title: '首页-热门' } } // 子路由2
]
}
];
|
- 在父组件(
Home.vue)中添加 <router-view> (子组件渲染容器)
1
2
3
4
5
6
7
8
9
10
11
12
|
<template>
<div class="home">
<h1>首页</h1>
<!-- 子路由导航 -->
<nav>
<router-link to="/home/recommend">推荐</router-link>
<router-link to="/home/hot">热门</router-link>
</nav>
<!-- 子组件渲染位置 -->
<router-view></router-view>
</div>
</template>
|
Vue3 路由的实现与应用
定义好路由规则后,需通过 “路由导航” 实现页面切换,并结合动态路由、懒加载等特性优化应用体验。
路由跳转(导航)
Vue Router 提供两种导航方式:router-link 组件(声明式)和编程式导航(JS 代码)。
声明式导航 (router-link)
router-link 是 Vue Router 提供的专用组件,替代传统的 <a> 标签,避免页面刷新。核心属性包括:
to: 目标路由(可传字符串或对象,如 to="/home" 或 to="{ path: '/article', query: { id: 1 } }")
replace:若添加该属性,跳转时会替换当前历史记录(而非新增),点击后退按钮不会返回当前页
active-class:路由激活时的 CSS 类名(默认 router-link-active)
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<template>
<nav>
<!-- 基础用法 -->
<router-link to="/home">首页</router-link>
<!-- 带query参数(URL中显示为?xxx=xxx) -->
<router-link to="{ path: '/article', query: { id: 123 } }">文章123</router-link>
<!-- 带params参数(需配合动态路由) -->
<router-link to="{ name: 'Article', params: { id: 456 } }">文章456</router-link>
<!-- replace属性:替换历史记录 -->
<router-link to="/login" replace>登录(不保留历史)</router-link>
<!-- 自定义激活类名 -->
<router-link to="/user" active-class="active">用户中心</router-link>
</nav>
</template>
<style>
/* 路由激活时的样式 */
.active {
color: #42b983;
border-bottom: 2px solid #42b983;
}
</style>
|
编程式导航
通过 useRouter() 获取路由实例,调用实例方法实现跳转,适用于 “点击按钮后跳转” 等交互场景,常用方法包括:
push:新增历史记录并跳转(等同于 router-link 默认行为)
replace:替换当前历史记录并跳转(等同于 router-link 的 replace 属性)
go:前进 / 后退指定步数(如 router.go(-1) 表示后退一页)
示例代码(组合式 API):
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
|
<template>
<button @click="goToArticle">查看文章</button>
<button @click="replaceToLogin">去登录(不保留历史)</button>
<button @click="goBack">后退</button>
</template>
<script setup>
import { useRouter } from 'vue-router';
const router = useRouter();
// 跳转至文章页(带params参数,需路由配置name)
const goToArticle = () => {
router.push({
name: 'Article', // 需在路由规则中配置name属性
params: { id: 789 }
});
};
// 替换历史记录跳转至登录页
const replaceToLogin = () => {
router.replace('/login');
};
// 后退一页
const goBack = () => {
router.go(-1);
};
</script>
|
动态路由实现
动态路由指 “在应用运行时,根据条件动态添加/删除路由规则”,适用于“权限控制”场景(如管理员可见的路由,普通用户不可见)。
动态添加路由
使用 router.addRoute() 方法动态添加路由,支持嵌套路由 (通过 parentName 指定父路由名称)。
示例代码:
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
|
// 假设在登录后根据用户角色动态添加路由
const addAdminRoutes = () => {
// 1. 定义管理员专属路由
const adminRoutes = [
{
path: '/admin',
name: 'Admin',
component: () => import('@/views/Admin.vue'),
meta: { requiresAuth: true, role: 'admin' },
children: [
{ path: 'user-manage', component: () => import('@/views/Admin/UserManage.vue') },
{ path: 'log-manage', component: () => import('@/views/Admin/LogManage.vue') }
]
}
];
// 2. 动态添加路由
adminRoutes.forEach(route => {
router.addRoute(route);
});
};
// 登录成功后调用(假设用户角色为admin)
const handleLogin = async () => {
const { role } = await loginAPI(/* 登录参数 */);
if (role === 'admin') {
addAdminRoutes(); // 添加管理员路由
}
};
|
动态删除路由
删除路由可通过以下方法:
router.removeRoute(routeName):通过路由名称删除
router.addRoute() 返回的函数:调用该函数删除对应路由
示例代码:
1
2
3
4
5
6
|
// 方法1:通过名称删除
router.removeRoute('Admin');
// 方法2:通过addRoute返回的函数删除
const removeRoute = router.addRoute({ path: '/temp', component: Temp });
removeRoute(); // 调用函数删除该路由
|
路由懒加载
路由懒加载 (Code Splitting) 指 “仅在路由被访问时才加载对应组件的代码”,可减少初始加载的 bundle 体积,提升应用启动速度。
Vue Router 中通过 “动态 import 语法” 实现。
实现方式
将路由规则中的 component 属性改为动态 import 函数:
1
2
3
4
5
6
7
8
9
10
11
12
|
const routes = [
{
path: '/home',
// 懒加载Home组件,打包时会单独生成一个chunk文件
component: () => import('@/views/Home.vue')
},
{
path: '/article/:id',
// 为懒加载的chunk命名(可选,便于调试)
component: () => import(/* webpackChunkName: "article" */ '@/views/Article.vue')
}
];
|
原理说明:
- 未使用懒加载时,所有组件代码会打包到一个
app.js 中,体积较大;
- 使用懒加载后,每个组件会被打包为独立的
chunk(如 home.js、article.js);
- 首次访问应用时,仅加载
app.js 和当前路由对应的 chunk;
- 访问其他路由时,浏览器会动态请求对应的
chunk 并执行,实现 “按需加载”
总结
Vue3 路由 (Vue Router 4) 通过 Hash/History 模式实现无刷新导航,借助导航守卫控制路由权限,支持动态路由、嵌套路由、懒加载等特性,是构建 Vue3 单页应用的核心工具。
掌握其原理需理解:
- 路由模式如何监听 URL 变化
- 导航守卫如何拦截与控制跳转
- 动态路由与懒加载如何优化应用性能