如题,结合官方文档与网络帖子,利用AI整理
Vue 路由
1. 安装与基础配置
Vue Router 是 Vue 3 构建单页面应用(SPA)的核心工具,第一步需完成安装与基础配置,确保路由功能正常运行:
-
安装:通过 npm 安装适配 Vue 3 的 Vue Router 4 版本,命令如下:
1
npm install vue-router@4
-
路由配置:在项目根目录创建
router/index.js文件,定义路由规则。核心是通过createRouter创建路由实例,createWebHistory设置 History 模式,同时导入需路由管理的组件(如 Home、About),并配置路由路径(path)、路由名称(name)及对应组件(component):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import { createRouter, createWebHistory } from 'vue-router';
import Home from '@/views/Home.vue';
import About from '@/views/About.vue';
const routes = [
{ path: '/', name: 'Home', component: Home },
{ path: '/about', name: 'About', component: About }
];
const router = createRouter({
history: createWebHistory(), // 采用 HTML5 History 模式
routes // 注入路由规则
});
export default router; -
注册路由:在项目入口文件
main.js中,导入路由实例并通过use(router)注册到 Vue 应用,确保路由功能全局可用:1
2
3
4
5import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
createApp(App).use(router).mount('#app'); -
路由出口与导航:在
App.vue中,使用<router-view>作为路由组件的渲染容器(匹配的路由组件会在此处渲染),使用<router-link>实现页面导航(替代传统<a>标签,避免页面刷新):1
2
3
4
5
6
7
8
9<template>
<div id="app">
<nav>
<router-link to="/">首页</router-link> <!-- 跳转到首页路由 -->
<router-link to="/about">关于</router-link> <!-- 跳转到关于路由 -->
</nav>
<router-view></router-view> <!-- 路由组件渲染出口 -->
</div>
</template>
2. 核心功能
Vue Router 提供 4 个核心功能,满足日常路由管理需求,包括动态路由、嵌套路由、编程式导航和路由懒加载:
-
动态路由:用于匹配同一类路径(如不同用户的详情页),通过
:在路由path中定义动态参数(如id),组件内通过useRoute()获取路由实例,再通过route.params访问动态参数:- 路由配置:
{ path: '/user/:id', component: User } - 组件内获取参数:
1
2
3
4
5<script setup>
import { useRoute } from 'vue-router';
const route = useRoute();
console.log(route.params.id); // 输出动态参数 id 的值
</script>
- 路由配置:
-
嵌套路由:实现路由的层级结构(如仪表盘下的个人资料、设置页面),通过
children字段在父路由中配置子路由,且父组件必须包含<router-view>以渲染子路由组件:1
2
3
4
5
6
7
8{
path: '/dashboard', // 父路由路径
component: Dashboard, // 父路由组件
children: [
{ path: 'profile', component: Profile }, // 子路由 1,访问路径 /dashboard/profile
{ path: 'settings', component: Settings } // 子路由 2,访问路径 /dashboard/settings
]
} -
编程式导航:不依赖
<router-link>,通过代码实现页面跳转,核心方法是router.push(),支持 4 种跳转形式:1
2
3
4
5
6
7
8// 1. 字符串路径:直接传入目标路由路径
router.push('/about');
// 2. 对象形式:传入含 path 的对象
router.push({ path: '/user/123' });
// 3. 命名路由 + 参数:需配合路由 name 和 params(推荐,参数更清晰)
router.push({ name: 'User', params: { id: '123' } });
// 4. 查询参数:通过 query 传递参数,URL 会显示为 /search?keyword=vue
router.push({ path: '/search', query: { keyword: 'vue' } }); -
路由懒加载:优化初始加载性能,减少首屏加载体积,通过动态导入(
() => import('组件路径'))的方式定义路由组件,只有当路由被访问时才会加载对应组件:1
{ path: '/admin', component: () => import('@/views/Admin.vue') }
3. 高级特性
Vue Router 提供 4 个高级特性,满足复杂场景下的路由控制与个性化需求:
-
导航守卫:控制路由跳转逻辑,分为 3 类,分别作用于全局、单个路由和组件内部:
- 全局守卫:在
router/index.js中定义,作用于所有路由,包括前置守卫(beforeEach,跳转前执行,需调用next()控制跳转)和后置守卫(afterEach,跳转后执行,无next()):1
2
3
4
5
6
7
8
9
10
11
12
13// 全局前置守卫:判断是否需要登录
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !isAuthenticated) { // to.meta.requiresAuth 来自路由元信息
next('/login'); // 未登录则重定向到登录页
} else {
next(); // 已登录则放行
}
});
// 全局后置守卫:修改页面标题
router.afterEach((to, from) => {
document.title = to.meta.title || '默认标题'; // 从路由元信息获取标题
}); - 路由独享守卫:在单个路由配置中定义,仅作用于该路由,通过
beforeEnter实现:1
2
3
4
5
6
7{
path: '/admin',
component: Admin,
beforeEnter: (to, from, next) => {
// 仅对 /admin 路由生效的验证逻辑
}
} - 组件内守卫:在组件内部定义,作用于当前组件对应的路由,常用
onBeforeRouteLeave(离开组件路由前执行):1
2
3
4
5
6
7
8
9
10
11
12
13<script setup>
import { onBeforeRouteLeave } from 'vue-router';
const unsavedChanges = true; // 模拟未保存的修改
onBeforeRouteLeave((to, from, next) => {
if (unsavedChanges) {
// 有未保存修改时,提示用户确认
confirm('确定离开?未保存的修改将丢失!') ? next() : next(false);
} else {
next(); // 无未保存修改则放行
}
});
</script>
- 全局守卫:在
-
路由元信息:为路由附加额外数据(如是否需要登录、页面标题),通过
meta字段在路由配置中定义,在导航守卫、组件中可通过to.meta或route.meta访问:1
2
3
4
5
6
7{
path: '/dashboard',
meta: {
requiresAuth: true, // 标记该路由需要登录才能访问
title: '控制台' // 该路由对应的页面标题
}
} -
滚动行为:自定义页面切换后的滚动位置,在创建路由实例时通过
scrollBehavior配置,支持 3 种场景:滚动到锚点、恢复之前滚动位置、滚动到顶部:1
2
3
4
5
6
7
8
9
10
11
12
13
14const router = createRouter({
scrollBehavior(to, from, savedPosition) {
if (to.hash) {
// 若目标路由有锚点(如 /page#section1),滚动到锚点位置,且平滑滚动
return { el: to.hash, behavior: 'smooth' };
} else if (savedPosition) {
// 若存在之前的滚动位置(如浏览器后退),恢复该位置
return savedPosition;
} else {
// 其他情况,默认滚动到页面顶部
return { top: 0 };
}
}
}); -
路由模式:控制 URL 的表现形式,分为 2 种模式,各有特点:
模式类型 核心方法 特点 适用场景 Hash 模式 createWebHashHistory()URL 中带 #(如http://xxx/#/home),兼容性好(支持所有浏览器),无需服务器配置对 URL 美观度要求不高、兼容性要求高的场景 History 模式 createWebHistory()URL 中无 #(如http://xxx/home),美观度高,但需服务器配置(如 Nginx 配置 fallback 到index.html),否则刷新会 404对 URL 美观度要求高、可配置服务器的场景
4. 常见场景处理
针对路由使用中的 3 种常见场景(404 页面、路由重定向、路由别名),提供具体解决方案:
-
404 页面:捕获所有未匹配的路由,显示 404 页面,通过通配符
/:pathMatch(.*)*配置路由路径,确保所有未定义的路由都指向 404 组件:1
{ path: '/:pathMatch(.*)*', component: NotFound } // NotFound 为自定义 404 组件
-
路由重定向:将一个路由路径重定向到另一个路由,通过
redirect字段配置,支持 2 种形式(重定向到固定路径、重定向到命名路由):1
2
3
4// 1. 重定向到固定路径:访问 /old 时,自动跳转到 /new
{ path: '/old', redirect: '/new' }
// 2. 重定向到命名路由:访问 /home 时,自动跳转到 name 为 Home 的路由
{ path: '/home', redirect: { name: 'Home' } } -
路由别名:为一个路由路径设置多个访问入口,通过
alias字段配置(支持字符串单个别名、数组多个别名),访问别名路径时,会渲染原路由的组件,且浏览器地址栏保持别名路径(不跳转):1
2
3
4
5
6
7
8// 配置 /dashboard 的别名为 /home、/main、/control-panel
const routes = [
{
path: '/dashboard', // 原路由路径
component: Dashboard, // 原路由组件
alias: ['/home', '/main', '/control-panel'] // 多个别名,访问任意别名都渲染 Dashboard 组件
}
];路由别名与重定向的核心区别:
特性 路由别名(alias) 路由重定向(redirect) URL 变化 地址栏显示别名路径,不变化 地址栏跳转到目标路径,发生变化 本质 多入口访问同一组件 路径跳转 场景 多语言支持、简化 URL、推广链接追踪 旧路径迁移、统一访问入口
5. 组合式 API 使用
Vue 3 中推荐使用组合式 API 操作路由,核心是 useRouter 和 useRoute 两个钩子,替代 Vue 2 中的 $router 和 $route:
-
核心 API 说明:
useRouter:获取路由实例,用于执行路由跳转操作(如push、replace);useRoute:获取当前路由信息,用于访问路由参数(query、params)、路由元信息(meta)等。
-
使用示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<script setup>
import { useRouter, useRoute } from 'vue-router';
// 获取路由实例和当前路由信息
const router = useRouter();
const route = useRoute();
// 1. 路由跳转:点击按钮跳转到 /about 路由
const goToAbout = () => {
router.push('/about');
};
// 2. 获取路由参数:获取 URL 中的 query 参数 id(如 /page?id=123)
console.log(route.query.id); // 输出 '123'
// 3. 获取动态路由参数:获取 /user/:id 中的 id(如 /user/456)
console.log(route.params.id); // 输出 '456'
</script>
6. 路由传参
Vue Router 提供 3 种路由传参方式,分别是 Query 参数、Params 参数和 Props 解耦,适用于不同场景:
-
Query 参数:通过 URL 的查询字符串(
?后的部分)传递参数,特点是参数可见、无需预先在路由配置中声明,适合传递非敏感、可选的参数(如筛选条件、分页信息):- 传递参数:通过
router.push()传入含query的对象:1
2
3
4
5
6
7
8
9import { useRouter } from 'vue-router';
const router = useRouter();
// 传递 keyword 和 sort 两个 Query 参数
router.push({
path: '/search', // 目标路由路径
query: { keyword: 'vue', sort: 'desc' } // Query 参数对象
});
// 生成的 URL:/search?keyword=vue&sort=desc - 接收参数:在目标组件中,通过
useRoute()获取route.query:1
2
3
4
5import { useRoute } from 'vue-router';
const route = useRoute();
const keyword = route.query.keyword; // 取值 'vue'
const sort = route.query.sort; // 取值 'desc'
- 传递参数:通过
-
Params 参数:通过 URL 路径的动态段传递参数,特点是参数需在路由配置中预先声明(用
:定义)、参数可见,适合传递必要、结构化的参数(如资源 ID):- 路由配置:先在
path中用:声明动态参数(如userId、postId):1
2
3
4
5
6
7const routes = [
{
path: '/user/:userId/post/:postId', // 声明 2 个动态参数:userId 和 postId
component: UserPost, // 目标组件
name: 'UserPost' // 路由名称,用于编程式导航
}
]; - 传递参数:支持 2 种方式(使用命名路由、使用路径拼接):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import { useRouter } from 'vue-router';
const router = useRouter();
const userId = 123;
const postId = 456;
// 方式 1:使用命名路由(推荐,参数与路径解耦)
router.push({
name: 'UserPost', // 匹配路由的 name
params: { userId, postId } // Params 参数对象,与路由声明的参数对应
});
// 方式 2:使用路径拼接(需手动拼接参数)
router.push(`/user/${userId}/post/${postId}`);
// 生成的 URL:/user/123/post/456 - 接收参数:在目标组件中,通过
useRoute()获取route.params:1
2
3
4
5import { useRoute } from 'vue-router';
const route = useRoute();
const userId = route.params.userId; // 取值 '123'(注意:Params 参数值为字符串类型)
const postId = route.params.postId; // 取值 '456'
- 路由配置:先在
-
Props 解耦:让组件不直接依赖
$route或useRoute(),通过props接收路由参数,提升组件复用性和可测试性,仅支持 Params 参数:- 路由配置:在路由中设置
props: true,Vue Router 会自动将 Params 参数转换为组件的 props:1
2
3
4
5{
path: '/user/:id', // 声明动态参数 id
component: UserDetail,
props: true // 开启 Props 传参,Params 参数会作为 props 传入组件
} - 组件接收:在组件中通过
props接收参数,像使用普通 props 一样使用:1
2
3
4
5
6
7
8
9
10
11// 选项式 API
export default {
props: ['id'], // 声明接收 id props
setup(props) {
console.log(props.id); // 直接使用参数,无需依赖 useRoute()
}
};
// 组合式 API(<script setup>)
const props = defineProps(['id']);
console.log(props.id); // 直接使用参数
- 路由配置:在路由中设置
-
三种传参方式对比:
对比维度 Query 参数 Params 参数 Props 解耦 URL 表现 ?key=value(查询字符串)/path/value(路径动态段)同 Params 参数 路由配置依赖 无需预先声明 需在 path 中用 :声明需在 path 中用 :声明,且路由需设props: true参数类型 字符串(自动转换基本类型) 字符串(需手动转换类型) 同 Params 参数,但通过 props 可定义类型 组件依赖 依赖 useRoute()依赖 useRoute()不依赖 useRoute(),依赖 props适用场景 可选参数、筛选条件、分页 资源 ID、必要标识符 组件复用性要求高的场景(如独立组件库)
7. 注意事项
使用 Vue Router 时需注意 3 个关键问题,避免功能异常或性能问题:
-
路由参数变化时组件复用:当路由参数变化(如从
/user/123跳转到/user/456),组件会复用(不会重新创建),若需监听参数变化,需使用beforeRouteUpdate守卫或监听route.params:1
2
3
4
5
6
7
8
9// 方式 1:使用 beforeRouteUpdate 守卫
onBeforeRouteUpdate((to, from) => {
console.log('参数变化:', to.params.id);
});
// 方式 2:监听 route.params
watch(() => route.params.id, (newId) => {
console.log('参数变化:', newId);
}); -
History 模式部署:History 模式下,刷新页面会向服务器发送请求,若服务器未配置 fallback 到
index.html,会返回 404。需配置服务器(如 Nginx):1
2
3location / {
try_files $uri $uri/ /index.html;
} -
导航守卫异步操作:若导航守卫中存在异步操作(如请求接口验证权限),需确保
next()仅调用一次,避免多次跳转。可通过async/await处理:1
2
3
4
5
6
7
8router.beforeEach(async (to, from, next) => {
const isAuthenticated = await checkAuth(); // 异步验证权限
if (to.meta.requiresAuth && !isAuthenticated) {
next('/login');
} else {
next();
}
});
关键问题
问题 1:Vue Router 中 Query 参数和 Params 参数的核心区别是什么?在实际项目中如何选择?
答案:
两者核心区别体现在 路由配置依赖、URL 表现 和 适用场景 上,具体如下:
-
路由配置依赖:Query 参数无需在路由
path中预先声明,直接通过router.push({path, query})传递;Params 参数必须在路由path中用:声明动态段(如/user/:id),否则无法传递。 -
URL 表现:Query 参数显示在 URL 的查询字符串中(如
/search?keyword=vue);Params 参数作为 URL 路径的一部分(如/user/123)。 -
参数稳定性:两者刷新页面后参数均保留,但 Query 参数更适合传递可选、非结构化数据,Params 参数更适合传递必要、结构化数据(如资源 ID)。
实际选择时:
-
若参数为 可选、非敏感(如筛选条件、分页页码、搜索关键词),选 Query 参数;
-
若参数为 必要、标识资源(如用户 ID、文章 ID),选 Params 参数,使 URL 更符合 RESTful 规范。
问题 2:Vue Router 的导航守卫有哪几类?分别适用于什么场景?
答案:
Vue Router 导航守卫分为 全局守卫、路由独享守卫 和 组件内守卫 三类,适用场景各有不同:
-
全局守卫:作用于所有路由,定义在
router/index.js中,包括beforeEach(跳转前)和afterEach(跳转后):- 适用场景:全局权限控制(如登录验证)、全局页面标题修改、全局埋点统计。
-
路由独享守卫:仅作用于单个路由,通过路由配置的
beforeEnter定义:- 适用场景:单个路由的特殊权限验证(如管理员页面额外验证角色)、特定路由的跳转限制。
-
组件内守卫:作用于组件对应的路由,定义在组件内部,常用
onBeforeRouteLeave(离开路由前):- 适用场景:组件内未保存修改的提示(如表单未提交时离开页面)、组件销毁前的资源清理(如取消请求)。
问题 3:Vue Router 中路由懒加载的实现原理是什么?如何结合路由分组进一步优化性能?
答案:
-
实现原理:路由懒加载基于 ES6 的 动态导入语法(
() => import('组件路径')),在初始打包时,懒加载的路由组件会被分割成独立的代码块(chunk),而非打包进主文件;只有当用户访问该路由时,浏览器才会异步加载对应的代码块,从而减少首屏加载体积,提升初始加载速度。 -
结合路由分组优化性能:当多个路由属于同一功能模块(如用户相关的
/user/login、/user/register、/user/profile),可通过 命名代码块 将它们分组打包,避免生成过多小代码块(减少 HTTP 请求数)。实现方式是在动态导入时添加/* webpackChunkName: "group-user" */注释(webpack 支持):1
2
3
4
5
6
7
8const routes = [
// 用户模块路由,打包为名为 "group-user" 的代码块
{ path: '/user/login', component: () => import(/* webpackChunkName: "group-user" */ '@/views/user/Login.vue') },
{ path: '/user/register', component: () => import(/* webpackChunkName: "group-user" */ '@/views/user/Register.vue') },
{ path: '/user/profile', component: () => import(/* webpackChunkName: "group-user" */ '@/views/user/Profile.vue') },
// 其他模块路由,打包为其他代码块
{ path: '/admin/dashboard', component: () => import(/* webpackChunkName: "group-admin" */ '@/views/admin/Dashboard.vue') }
];优化效果:访问用户模块任意路由时,会加载 “group-user” 代码块,后续访问该模块其他路由无需重复加载,减少 HTTP 请求,提升后续访问速度。