Vue Router 通过跳转或取消的方式守卫导航,可以在导航解析的不同节点来控制路由的跳转与取消。定义路由时,可以配置元信息。可以定制页面跳转的过度效果。还可以在程序已经运行的时候添加和删除路由。
1 Router进阶
1.1 导航守卫
守卫,是指通过某种方式来控制路由的跳转与取消。可以全局的、单个路由独享的,或者组件级的方式来提供守卫。
图 完整的导航解析流程。
从beforeRouteLeave 到 beforeResolve,都可以控制路由的跳转与取消。
导航结果 | 返回值 |
正常导航 | 返回undefined、true或没有返回 |
跳转到其他路由 | 返回路径的字符串,或者一个对象(包含name字段,用来指示路由)。 |
停留在当前路由 | 返回false。 |
表 守卫的返回值与控制路由的跳转
// 守卫的不同返回值
router.beforeEach((to,from) => {
const name = to.name
if (name === 'detail') {
return false; // 将不会跳转
}
if (name === 'other1') {
return '/other' // 跳转到这个路由
}
if (name === 'other2) {
return {name: 'user'} // 跳转到名为user的路由
}
// 正常跳转到目标路由,等效于 return true 或 return undefined
})
// 在定义路由时,为专属路由定义beforeEnter守卫
{
path: 'order/:id',
name: 'userOrder',
component: () => import("@/views/UserOrder.vue"),
beforeEnter: (to,from) => {
console.log('userOrder route,beforeEnter',to,from)
}
}
// 在组合式Api中,定义beforeRouteLeave、beforeRouteUpdate 守卫
<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
onBeforeRouteLeave((to,from) => {
})
</script>
1.2 动态路由
在程序已经运行时可以添加或删除路由,主要是通过router.addRoute() 和 router.removeRoute()这两个函数来实现。
// 在守卫中动态添加路由
router.beforeEach((to,from) => {
if (to.name === 'goodsDetail' && !router.hasRoute('goodsDetail')) {
// 这里添加的时一个嵌套路由,如果不是嵌套路由,则把第一个参数删除
router.addRoute('goodsList',{name: 'goodsDetail',path: 'detail', component: () => import('@/views/GoodsDetail.vue')})
return to.fullPath // 添加完路由后,它仍不会导航到目标路由,需要手动return 一下
}
})
当路由被删除时,所有的别名和子路由也会被同时删除。删除路由的不同方法:
- 调用router.removeRoute()方法,参数是需要被删除路由的名称。
- 通过调用reouter.addRoute()返回的回调:
- 添加一个名称冲突的路由,如果添加与现有途径名称相同的路由,会先删除这个路由,再添加新的路由。
const removeRoute = router.addRoute(路由)
removeRoute();// 会删除这个路由
1.3 导航故障
如果导航被阻止,导致用户停留在同一个页面上,由router.push返回的Promise解析值将是Navigation Failure。 否则返回值为undefined。
isNavigationFailure 用来检测这个错误类型。
const result = await router.push(‘/other’)
if (isNavigationFailure (result,NavigationFailureType.aborted)) {}
枚举值 | 错误情况 |
aborted | 在导航守卫中返回false,中断了本次导航。 |
cancelled | 在当前导航完成之前又有了一个新的导航。 |
duplicated | 目标位置是当前位置 |
表 NavigationFailureType 的枚举值对应的错误情况
1.4 扩展RouterLink
通过自定义一个RouterLink,来满足某些业务场景。
<script lang="ts" setup>
import {computed, defineProps} from "vue";
import {RouterLink} from "vue-router";
const props = defineProps({
...RouterLink.props,
inactiveClass: String
})
const isExternalLink = computed(() => {
return typeof props.to === 'string' && props.to.startsWith('http')
})
</script>
<template>
<a v-if="isExternalLink" v-bind="$attrs" :href="to" target="_blank">
<slot />
</a>
<router-link
v-else
v-bind="$props"
custom
v-slot="{ isActive, href, navigate }"
>
<a v-bind="$attrs" :href="href" @click="navigate" :class="isActive ? activeClass : inactiveClass">
<slot />
</a>
</router-link>
</template>