前言
vue-router4x相对于vue-router3x除了新增了组合式API以外,还删除或变动了不少地方。单独列出来变动点太杂乱。这里系统性的把项目中经常能用到的知识点进行整理
一、安装
yarn add vue-router
二、基本使用
1. 定义路由
新建router/routes.ts
const routes = [
{
path: "/login",
component: () => import("@/pages/login.vue"), //路由懒加载
},
{
path: "/home",
component: () => import("@/pages/home.vue"),
},
];
export default routes;
2. 创建路由实例
新建router/index.ts
import { createRouter, createWebHistory } from "vue-router";
import routes from "./routes";
const router = createRouter({
history: createWebHistory(), //可传参数,配置base路径,例如'/app'
routes,
});
export default router;
3. 路由注册
修改main.ts
import router from "./router/index";
const app = createApp(App);
app.use(router); //注册路由
4. 定义路由出口
修改App.vue
<template>
<router-view v-slot="{ Component }">
<Transition name="fade" mode="out-in">
<component :is="Component" />
</Transition>
</router-view>
</template>
<style>
.fade-enter-active,
.fade-leave-active {
transition: all 0.2s ease;
}
.fade-enter-from,
.fade-leave-active {
opacity: 0;
}
</style>
router-view 将显示与 url 对应的组件,可以把它放在任何地方以适应布局
Transition 是基于路由的动态过渡动效
三、嵌套路由
在App.vue中定义的router-view,这是顶层的出口,渲染最高级路由匹配到的组件
如果要实现登录之后左侧菜单栏不变,右侧随路由的切换变化显示的内容,需使用嵌套路由
1. 定义路由配置文件
修改router/routes.ts
const routes = [
{
path: "/login",
component: () => import("@/pages/login.vue"),
},
{
path: "/home",
component: () => import("@/pages/home.vue"),
children: [
{
path: "/home/user",
component: () => import("@/pages/user.vue"),
},
{
path: "/home/manage",
component: () => import("@/pages/manage.vue"),
},
],
},
];
export default routes;
2. 定义嵌套路由入口
修改pages/home.vue
<template>
<div>
菜单栏
<router-view v-slot="{ Component }">
<Transition name="fade" mode="out-in">
<component :is="Component" />
</Transition>
</router-view>
</div>
</template>
访问 http://127.0.0.1:5173/home/manage 与 http://127.0.0.1:5173/home/user可查看效果
四、配置404页面
修改router/routes.ts
const routes = [
...//添加(放在最后)
{
path: "/:pathMatch(.*)*",
component: () => import("@/pages/notFound.vue"),
},
];
五、声明式、编程式导航
1. 声明式导航(在模板中进行路由跳转)
<router-link to="/home"> 跳转home </router-link>;
2. 编程式导航(组合式API)
<script setup lang="ts">
import { useRouter } from 'vue-router';
const router = useRouter();
const handleManage = () => {
router.push('/home/manage');
};
</script>
六、重定向
情景:在嵌套路由中,当访问/home时想重定向到/home/user
修改router/routes.ts
{
path: '/home',
component: () => import('@/pages/home.vue'),
redirect: '/home/user', //新增
children: [
{
path: '/home/user',
component: () => import('@/pages/user.vue'),
},
{
path: '/home/manage',
component: () => import('@/pages/manage.vue'),
},
],
},
当访问http://127.0.0.1:5173/home 时,会自动重定向访问 http://127.0.0.1:5173/home/user
七、路由传参
1. query传参
//页面传参
<script setup lang="ts">
import { useRouter } from 'vue-router';
const router = useRouter();
const handleManage = () => {
router.push({
path: '/home/manage',
query: {
plan: '123',
},
});
};
</script>
//页面接参
<script setup lang="ts">
import { useRoute } from 'vue-router';
const route = useRoute();
console.log(route.query.plan); //query接参
</script>
2. 动态路由匹配
//定义路由
{
path: '/register/:plan', // 动态字段以冒号开始
component: () => import('@/pages/register.vue'),
},
//页面传参
<script setup lang="ts">
import { useRouter } from 'vue-router';
const router = useRouter();
const handleManage = () => {
router.push('/register/123');
};
</script>
//页面接参
<script setup lang="ts">
import { useRoute } from 'vue-router';
const route = useRoute();
console.log(route.params.plan); //params接参
</script>
3. 命名路由params传参(已被废弃)
八、导航守卫
1. 全局前置守卫
使用场景:做登录判断,未登陆用户跳转到登录页
修改router/index.ts
router.beforeEach((to, from) => {
if (to.path === '/login') {
//在登录页做清除操作,如清除token等
}
if (!localStorage.getItem('token') && to.path !== '/login') {
// 未登陆且访问的不是登录页,重定向到登录页面
return '/login';
}
});
2. 路由独享守卫
使用场景:部分页面不需要登录,部分页面需要登录才能访问
修改router/routes.ts
const auth = () => {
if (!localStorage.getItem("token")) {
// 未登陆,重定向到登录页面
return "/login";
}
};
const routes = [
...{
path: "/home",
component: () => import("@/pages/home.vue"),
redirect: "/home/user",
children: [
{
path: "/home/user",
component: () => import("@/pages/user.vue"),
},
{
path: "/home/manage",
component: () => import("@/pages/manage.vue"),
beforeEnter: auth, //路由独享守卫
},
],
},
];
3. 组件内守卫
使用情景:预防用户在还未保存修改前突然离开。该导航可以通过返回 false 来取消
<script setup lang="ts">
import { onBeforeRouteLeave } from 'vue-router';
// 与 beforeRouteLeave 相同,无法访问 `this`
onBeforeRouteLeave((to, from) => {
const answer = window.confirm('确定离开吗');
// 取消导航并停留在同一页面上
if (!answer) return false;
});
</script>
九、路由元信息
将自定义信息附加到路由上,例如页面标题,是否需要权限,是否开启页面缓存等
使用情景:使用路由元信息+全局前置守卫实现部分页面不需要登录,部分页面需要登录才能访问
修改router/routes.ts
const routes = [
...{
path: "/home",
component: () => import("@/pages/home.vue"),
redirect: "/home/user",
children: [
{
path: "/home/user",
component: () => import("@/pages/user.vue"),
},
{
path: "/home/manage",
component: () => import("@/pages/manage.vue"),
meta: {
title: "管理页", // 页面标题
auth: true, //需要登录权限
},
},
],
},
];
修改router/index.ts
router.beforeEach((to, from) => {
if (!localStorage.getItem("token") && to.meta.auth) {
// 此路由需要授权,请检查是否已登录
// 如果没有,则重定向到登录页面
return {
path: "/login",
// 保存我们所在的位置,以便以后再来
query: { redirect: to.fullPath },
};
}
});
十、路由懒加载
使用() => import()方式导入的组件,只会在第一次进入页面时才会加载对应路由的组件
const UserDetails = () => import('./views/UserDetails.vue')
const router = createRouter({
// ...
routes: [{ path: '/users/:id', component: UserDetails }],
})
webpack命名chunk
const UserDetails = () => import(/* webpackChunkName: "group-user" */ './UserDetails.vue')
vite会自动根据组件文件名命名chunk
十一、router-link
router-link 组件默认为a标签,在vue router 3.x中,可通过tag属性更改标签名,event属性更改事件名
在vue router 4.x中,这两个属性已被删除,通过作用域插槽(子组件给父组件传值的插槽)实现自定义导航标签
示例:将导航标签改为div,且需双击触发
<router-link v-slot="{ href, navigate, isExactActive }" to="/home/user" custom>
<div :class="{ active: isExactActive }" :href="href" @dblclick="navigate">跳转user</div>
</router-link>
十二、动态路由
与动态路由匹配不同,动态路由是手动添加路由表中没有的路由,通常用在权限校验中,如果没有该权限,直接访问该路由失败
- 修改router/routes.ts
const routes = [
{
path: "/login",
component: () => import("@/pages/login.vue"),
},
{
path: "/register/:plan",
component: () => import("@/pages/register.vue"),
},
{
path: "/home",
name: "Home", //增加name,动态路由通过name挂载到该子路由下
component: () => import("@/pages/home.vue"),
redirect: "/home/user",
children: [
{
path: "/home/user",
component: () => import("@/pages/user.vue"),
},
],
},
{
path: "/:pathMatch(.*)*",
component: () => import("@/pages/notFound.vue"),
},
];
//将/home/manage拆出来
export const manageRoute = {
path: "/home/manage",
component: () => import("@/pages/manage.vue"),
};
export default routes;
- 修改pages/login.vue
<script setup lang="ts">
localStorage.setItem('role', 'admin'); //在登录页存储用户等级
</script>;
- 修改App.vue
//新增
<script setup lang="ts">
import { watch } from "vue";
import { useRouter, useRoute } from "vue-router";
import { manageRoute } from "@/router/routes";
const router = useRouter();
const route = useRoute();
watch(route, async (newVal) => {
const role = localStorage.getItem("role");
if (role && role === "admin") {
router.addRoute("Home", manageRoute);
/* 防止页面刷新,路由丢失 */
/* 在动态路由页面刷新时,matched数组为空 */
if (!newVal.matched.length && newVal.fullPath === "/home/manage") {
await router.replace("/home/manage");
}
}
});
</script>
如果localStorage.getItem(‘role’)的值不为admin,直接访问/home/manage,会返回404页面