基础
创建路由实例
我们使用的版本是4版本以上的语法,我们在探讨路由的时候也采用4以及4以上的语法进行学习。
import { createMemoryHistory, createRouter } from 'vue-router'
import HomeView from './HomeView.vue'
import AboutView from './AboutView.vue'
const routes = [
{ path: '/', component: HomeView },
]
const router = createRouter({
history: createMemoryHistory(),
routes,
})
其中routes定义路由本身,目的是将路由映射到组件。
history: createWebHashHistory()使用哈希的模式,实际 URL 之前使用一个哈希字符 (#) 来内部传递。由于 URL 的这一部分永远不会发送到服务器,因此它不需要服务器级别的任何特殊处理。history: createWebHistory()的URL 将看起来“正常”,但是这个模式下我们需要配合通配符进行相关的配置history: createMemoryHistory(),需要你在调用app.use(router)后推送初始导航。非常适合 Node 环境和 SSR
进行路由注入
const app = createApp(App);
app.use(router).mount('#app');
或者
const app = createApp(App)
app.use(router)
app.mount('#app')
注意 对 use() 的调用需要在对 mount() 的调用之前进行。
访问路由和当前路由
我们使用Vue3,所以我们常用的是组合式的API,具体的写法如下:
<script setup>
import { computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
const router = useRouter() // 可以进行路由跳转
const route = useRoute() // 可以查询路由相关信息
</script>
我们经常将路由器实例称为 router,这是 createRouter() 返回的对象。当前路由将被称为 route。
带参数的动态路由匹配
我们可能有一个 User 组件,它应该为所有用户渲染,但使用不同的用户 ID,我们通过 动态路由
import User from './User.vue'
// these are passed to `createRouter`
const routes = [ { path: '/users/:id', component: User } ]
一个 参数 由冒号 : 表示。当路由匹配时,其 参数 的值将作为 route.params 公开给每个组件。因此,我们可以通过将 User 的模板更新为以下内容来渲染当前用户 ID
路由匹配语法
参数使用自定义表达式
我们有一种场景,我们需要在路由上取两个值,但是这两个值要根据类型放到不同的变量中,这种时候我们有2种解决方案。
方案一: 使用不同的静态路由进行区分,代码如下:
const routes = [
// matches /o/3549
{ path: '/o/:orderId' },
// matches /p/books
{ path: '/p/:productName' },
]
方案二:使用自定义的表达式进行区分,代码如下:
const routes = [
// /:orderId -> matches only numbers
{ path: '/:orderId(\\d+)' },
// /:productName -> matches anything else
{ path: '/:productName' }, ]
可重复参数
你需要匹配具有多个部分的路由,例如 /first/second/third,你应该使用 *(0 个或多个)和 +(1 个或多个)将参数标记为可重复的,代码如下:
const routes = [
// /:chapters -> matches /one, /one/two, /one/two/three, etc
{ path: '/:chapters+' },
// /:chapters -> matches /, /one, /one/two, /one/two/three, etc
{ path: '/:chapters*' },
]
这些也可以与自定义正则表达式结合使用,方法是在右括号后添加它们
const routes = [
// only match numbers // matches /1, /1/2, etc
{ path: '/:chapters(\\d+)+' },
// matches /, /1, /1/2, etc
{ path: '/:chapters(\\d+)*' },
]
敏感和严格路由选项
const router = createRouter({
history: createWebHistory(),
routes: [
// will match /users/posva but not:
// - /users/posva/ because of strict: true
可选参数
可以使用 ? 修饰符(0 个或 1 个)将参数标记为可选,例子如下:
const routes = [
// will match /users and /users/posva
{ path: '/users/:userId?' },
// will match /users and /users/42
{ path: '/users/:userId(\\d+)?' },
]
路由命名
创建路由的时候可以给路由直接命名,如下面的配置
const routes = [
{
path: '/user/:username',
name: 'profile',
component: User
}
]
我们可以使用 name 而不是 path,将 to 属性传递给 <router-link>
使用 name 有多种优势
- 没有硬编码的 URL。
- 自动编码
params。 - 避免 URL 键入错误。
- 绕过路径排名,例如显示与相同路径匹配的排名较低的路由。
嵌套路由
假设我们创建下面的路由来作为例子
<template>
<router-view />
</template>
<template>
<div class="user">
<h2>User {{ $route.params.id }}</h2>
<router-view />
</div>
</template>
要将组件渲染到这个嵌套的 router-view 中,我们需要在任何路由中使用 children 选项。这个时候我们使用的配置需要修改到如下模式
const routes = [
{
path: '/user/:id',
component: User,
children: [
{
// UserProfile will be rendered inside User's <router-view>
// when /user/:id/profile is matched
path: 'profile',
component: UserProfile,
},
{
// UserPosts will be rendered inside User's <router-view>
// when /user/:id/posts is matched
path: 'posts',
component: UserPosts,
},
],
},
]
以 / 开头的嵌套路径将被视为根路径。
编程导航
要导航到不同的 URL,请使用 router.push。此方法将一个新条目推入历史堆栈,因此当用户单击浏览器后退按钮时,他们将被带回上一个 URL。
实际操作
定义基础路由表
我们需要先定义一份基础路由。这部分路由是不需要做权限限制的,我们任何时候都可以访问本路由,例如我们的登陆页面,404页面。
const routes =[
{
path: '/login',
name: 'login',
component: () => import(/* webpackChunkName: 'login' */ '@/views/login/index.vue'),
},
{
path: '/NotFound',
name: '404',
component: () => import(/* webpackChunkName: 'login' */ '@/views/NotFound/index.vue'),
},
]
获取动态路由
定义addRoute()方法,在登录后获取服务器路由通过这个方法添加路由,首先我们和后端获取到对路由定义的返回结构和字段,方便我们后续进行组装。
import Layout from '@/views/components/layout/index.vue';
const result =[
{
path: '/',
name: 'Layout',
component: Layout,
children: [
path: 'info',
name: 'userInfo',
component: 'user/info'
}
]
添加路由
我们假设现在的登陆成功后,我们请求到了路由,然后我们将路由注入到我们现在从口中拿到的路由。
import { ref } from 'vue'
import { useRouter } from 'vue-router'
const res_route = ref([])
const addRoute = async () => {
// 当本地路由没有值的时候从后端获取路由
if (!res_route.value.length) {
// 从后段接口获取路由
const res = await getRoutes()
res_route.value = res
// 把路由表的component字段转成真实的路由
server_route.value.map((_route) => {
if (_route.component === 'Layout') {
break;
}
const children = _route.children
// 根据字符串动态导入路由组件,这个时候表示数组中是有值的,不是一个空数组
if (Array.isArray(children) && children?.length > 0) {
children.map((childRoute) => {
childRoute.component = () => import(`@/views/${childRoute.path}.vue`)
}
}
}
}
}
server_route.value.map((route) => router.addRoute(route))
这个时候我们已经完成了路由的注入,但是我们的项目需要一些比较基础简单的功能,如:没有登录的用户需要重新跳转到登录页面,这个时候我们需要用到路由守卫功能,来满足我们的功能,结合上面的功能,我们将代码梳理一下。
router.beforeEach(async (to, from, next) => {
const token = localStorage.getItem('token');
if (token && token !== 'null') {
if (!store.state.userInfo) {
// 根据roles权限生成可访问的路由表
server_route.value.map((route) => router.addRoute(route))
let f_path = '';
if (accessRoutes.length > 0) {
const info = accessRoutes[0];
f_path += info.path;
if (info.children && info.children.length > 0) {
f_path += '/' + info.children[0].path;
}
}
const p_info = accessRoutes.find((item) => {
return to.path.startsWith(item.path);
});
if ((to.path == '/' || !p_info) && f_path) {
next({
path: f_path,
replace: true,
}); // hack方法 确保addRoutes已完成
} else {
next({
...to,
replace: true,
}); // hack方法 确保addRoutes已完成
}
} else {
next();
}
} else {
if (to.path === '/login') {
//如果是登录页面路径,就直接next()
next();
} else {
//不然就跳转到登录;
next('/login');
}
}
});