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-linkv-elsev-bind="$props"customv-slot="{ isActive, href, navigate }"><a v-bind="$attrs" :href="href" @click="navigate" :class="isActive ? activeClass : inactiveClass"><slot /></a></router-link>
</template>