# 前言
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 页面