Vue Router 路由导航与守卫完整用法详解
Api 文档地址
[https://next.router.vuejs.org/zh/api/]
router-view
router-view 将显示与 url 对应的组件。
测试示例
当路由配置如下时,代表进入根路径 / 会重定向到 /home 路径
const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/home',
},
/** 主页 */
{
path: '/home',
name: 'Home',
component: Home,
},
];
App.vue 文件的代码如下
<template>
<router-view />
</template>
- 当
App.vue中加入router-view标签即可渲染/home路径对应的组件Home - 去掉时会什么也不显示
router-link && push && replace
router-link内部会立刻把to的值传到router.push()active-class:链接激活时,应用于渲染的a标签的class。默认值:router-link-active(或者全局linkActiveClass)exact-active-class:链接精准激活时,应用于渲染的a标签的class。默认值:router-link-exact-active(或者全局linkExactActiveClass)
router.push和所有其他导航方法都会返回一个Promise,让我们可以等到导航完成后才知道是成功还是失败。push:向 history 栈添加一个新的记录。replace:通过替换历史堆栈中的当前entry,它在导航时不会向history添加新记录。函数签名:
to RouteLocationRaw 要导航到的路由地址
push/replace(to: RouteLocationRaw): Promise<NavigationFailure | void | undefined>
示例
<router-link to="/">跳转至根路径</router-link>
<br />
createRouter
创建一个可以被 Vue 应用程序使用的路由实例。
示例
const router = createRouter({
/** 用于路由实现历史记录 */
history: createWebHistory(),
linkActiveClass: 'par-custom-active-class',
linkExactActiveClass: 'par-custom-exact-active-class',
/** 应该添加到路由的初始路由列表 */
routes,
/** 在页面之间导航时控制滚动的函数 */
scrollBehavior: () => ({
top: 0,
left: 0,
}),
});
参数 RouterOptions
history: 用于路由实现历史记录 createWebHistory | createWebHashHistory(基于 hash 的历史记录) linkActiveClass: 用于激活的 RouterLink 的默认类。如果什么都没提供,则会使用 router-link-activelinkExactActiveClass: 用于精准激活的 RouterLink 的默认类。如果什么都没提供,则会使用 router-link-exact-active。 routes:应该添加到路由的初始路由列表。 scrollBehavior:在页面之间导航时控制滚动的函数。可以返回一个 Promise 来延迟滚动
scrollBehavior 参数详解
在页面之间导航时控制滚动的函数。可以返回一个 Promise 来延迟滚动,可以自定义路由切换时页面如何滚动。
- 始终滚动到左上角
scrollBehavior: () => ({
top: 0,
left: 0,
});
- 通过
el传递一个CSS选择器或一个DOM元素。在这种情况下,top和left将被视为该元素的相对偏移量。
scrollBehavior(to, from, savedPosition);
{
// 始终在元素 #main 上方滚动 10px
return {
// 也可以这么写
// el: document.getElementById('main'),
el: '#main',
top: -10,
};
}
- 模拟 滚动到锚点 + 滚动行为 + 延迟滚动 路由访问的路径如:
http://localhost:3000/pushDemo#pushDemo在测试的vue页面需要增加如下代码
<a id="pushDemo">这是一个 pushDemo 锚点</a>
// 路由不能用 hash 模式
history: (createWebHistory(), scrollBehavior(to, from, savedPosition));
{
return new Promise((resolve, reject) => {
setTimeout(() => {
if (to.hash) {
console.log(to.hash, 999);
resolve({
el: to.hash,
behavior: 'smooth',
});
}
}, 2500);
});
}
params
- 使用
params携带参数 params不能与path一起使用
<p>使用 params 携带参数</p>
<p @click="$router.push('/users/mio')">跳转到 user/mio 页面</p>
<p @click="$router.push({ path: '/users/miofly' })">跳转到 user/miofly 页面</p>
<p
@click="$router.push({ name: 'User', params: { id: 'wfd', sex: 'boy' } })"
>跳转到 user/wfd 页面</p>
<p
@click="$router.push({ name: 'User', params: { id: 'fnn', sex: 'girl' } })"
>跳转到 user/fnn 页面</p>
next
next():进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是confirmed(确认的)。next(false):中断当前的导航。如果浏览器的URL改变了(可能是用户手动或者浏览器后退按钮),那么URL地址会重置到from路由对应的地址。next('/')或者next({path:'/'}):跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向next传递任意位置对象,且允许设置诸如replace:true、name:'home'之类的选项以及任何用在router-link的toProp或router.push中的选项。next(error) :如果传入 next 的参数是一个Error实例,则导航会被终止且该错误会被传递给router.onError()注册过的回调。
路由导航失败的情况
router.push返回的promise,vue 的路由导航是异步的- 有三种情况会中断导航,可以用 isNavigationFailure 和 NavigationFailureType 来区分。
aborted:在导航守卫中返回false中断了本次导航。cancelled: 在当前导航还没有完成之前又有了一个新的导航。比如,在等待导航守卫的过程中又调用了router.push。duplicated:导航被阻止,因为我们已经在目标位置了。
NavigationFailureType
包含所有可能导航失败类型的枚举,可以传递给 isNavigationFailure 来检查某些特定类型的失败。不要使用任何数值,总是使用诸如 NavigationFailureType.aborted 这样的变量。
export declare enum NavigationFailureType
| 成员 | 值 | 描述 |
|---|---|---|
| aborted | 4 | 终止导航是指由于导航守卫返回 false 或调用 next(false) 而失败的导航。 |
| cancelled | 8 | 取消导航是指由于最近的导航完成启动(不一定是完成)而失败的导航。 |
| duplicated | 16 | 重复导航是指在启动时已经在同一位置失败的导航。 |
示例
<p>导航故障测试</p>
<p @click="goPageTestOne">再次跳转当前路由</p>
<p @click="goPageTestTwo">在路由守卫中再次跳转其他路由</p>
<p @click="goPageTestThree">在路由守卫阻止跳转</p>
<script lang="ts">
import { defineComponent } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { NavigationFailureType, isNavigationFailure } from 'vue-router';
export default defineComponent({
setup(prop) {
// 返回 router 实例。相当于在模板中使用 $router。必须在 setup() 中调用。
const router = useRouter();
// 返回当前路由地址。相当于在模板中使用 $route。必须在 setup() 中调用。
const route = useRoute();
async function goPageTestOne() {
const status = await router.push(route.fullPath);
if (isNavigationFailure(status, NavigationFailureType.duplicated)) {
console.log('已经在当前路由中');
}
}
async function goPageTestTwo() {
const status = await router.push('/test/one');
if (isNavigationFailure(status, NavigationFailureType.cancelled)) {
console.log(
'在当前导航还没有完成之前又有了一个新的导航。比如,在等待导航守卫的过程中又调用了 router.push。',
);
}
}
async function goPageTestThree() {
const status = await router.push('/test/two');
if (isNavigationFailure(status, NavigationFailureType.aborted)) {
console.log('在导航守卫中返回 false 中断了本次导航。');
}
}
return {
goPageTestOne,
goPageTestTwo,
goPageTestThree,
};
},
});
</script>
路由文件
{
path: '/test',
component: Test,
// beforeEnter: (to, from) => false,
children: [
{
path: 'one',
name: 'TestOne',
component: TestOne,
beforeEnter: (to, from) => {
router.push('/')
},
},
{
path: 'two',
name: 'TestTwo',
component: TestTwo,
beforeEnter: (to, from) => false,
}
]
},
START_LOCATION
路由所在的初始路由地址。可用于导航守卫中,以区分初始导航。
示例
{
"path"
:
"/",
"params"
:
{}
,
"query"
:
{}
,
"hash"
:
"",
"fullPath"
:
"/",
"matched"
:
[],
"meta"
:
{}
}
路由守卫
- 全局路由守卫
router.beforeEach((to, from, next) => {
console.log('调用了 router.beforeEach 全局前置守卫');
});
router.beforeResolve((to, from, next) => {
console.log('调用了 router.beforeResolve 全局解析守卫');
});
// 和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:
router.afterEach((to, from) => {
console.log('调用了 router.afterEach 全局后置钩子');
});
- 路由独享守卫
{
path: '/routerGurad/:name',
component: RouterGurad,
// beforeEnter 守卫 只在进入路由时触发,不会在 params、query 或 hash 改变时触发。
// 例如,从 /users/2 进入到 /users/3 或者从 /users/2#info 进入到 /users/2#projects。
// 它们只有在 从一个不同的 路由导航时,才会被触发。
beforeEnter: (to, from) => {
console.log('调用了 beforeEnter 路由独享的守卫');
},
},
- 组件内守卫+生命周期 (routerGurad 路由页面)
import {
defineComponent,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onMounted,
onUnmounted,
onUpdated,
} from 'vue';
import { onBeforeRouteLeave, onBeforeRouteUpdate, useRouter } from 'vue-router';
export default defineComponent({
beforeCreate() {
console.log('beforeCreate()');
},
created() {
console.log('created');
},
beforeMount() {
console.log('beforeMount');
},
mounted() {
console.log('mounted');
},
beforeUpdate() {
console.log('beforeUpdate');
},
updated() {
console.log('updated');
},
beforeUnmount() {
console.log('beforeUnmount');
},
unmounted() {
console.log('unmounted');
},
setup(prop) {
console.log('setUp 触发');
onBeforeMount(() => {
console.log('--onBeforeMount');
});
onMounted(() => {
console.log('--onMounted');
});
onBeforeUpdate(() => {
console.log('--onBeforeUpdate');
});
onUpdated(() => {
console.log('--onUpdated');
});
onBeforeUnmount(() => {
console.log('--onBeforeUnmount');
});
onUnmounted(() => {
console.log('--onUnmounted');
});
// 对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持 传递回调,因为没有必要了:
onBeforeRouteLeave((to, from) => {
// 在导航离开渲染该组件的对应路由时调用
// 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
console.log('调用了 BeforeRouteLeave 组件守卫');
});
onBeforeRouteUpdate((to, from) => {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
// 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
console.log('调用了 BeforeRouteUpdate 组件守卫');
});
},
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被验证前调用
// 不能获取组件实例 `this` !
// 因为当守卫执行时,组件实例还没被创建!
// 不过,你可以通过传一个回调给 next 来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数:
console.log('调用了 beforeRouteEnter 组件守卫');
next(vm => {
console.log('beforeRouteEnter守卫中的 next 调用');
});
},
});
完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用
beforeRouteLeave守卫。 - 调用全局的
beforeEach守卫。 - 在重用的组件里调用
beforeRouteUpdate守卫 - 在路由配置里调用
beforeEnter。 - 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter。 - 调用全局的
beforeResolve守卫 - 导航被确认。
- 调用全局的
afterEach钩子。 - 触发
DOM更新。 - 调用
beforeRouteEnter守卫中传给next的回调函数,创建好的组件实例会作为回调函数的参数传入。
示例一: index 路由页面
<div @click="$router.push('/routerGurad/wfd')">
跳转到 routerGurad 页面
</div>
<script lang="ts">
import { defineComponent } from 'vue';
import { onBeforeRouteLeave, onBeforeRouteUpdate, useRouter } from 'vue-router';
export default defineComponent({
setup(prop) {
const router = useRouter();
onBeforeRouteLeave((to, from) => {
console.log('调用了 onBeforeRouteLeave');
});
onBeforeRouteUpdate((to, from) => {
console.log('调用了 onBeforeRouteUpdate');
});
return {};
},
});
</script>
从 index页面 跳转至 routerGurad 页面

示例二: routerGurad 自跳转
<template>
<div>
<h1>这是测试路由导航守卫的页面</h1>
<div @click="$router.push('/routerGurad/fnn')">
跳转到 routerGurad fnn 页面
</div>
<div @click="$router.push('/routerGurad/wfd')">
跳转到 routerGurad wfd 页面
</div>
</div>
</template>

嵌套路由
- 配置嵌套路由时,子路由的
path为空, 则父路由默认访问的就是为空的路由,/child - 子路由的
path为不带/的字符串时,路由的访问path是父路由path+ 子路由path - 当子路由的 path 为带
/的字符串时,路由的访问path就是子路由pathrouter配置
{
path: '/child',
component: ChildIndex,
children: [
{
path: '',
component: ChildFour,
},
{
path: 'one',
component: ChildOne,
},
{
path: 'two',
component: ChildTwo,
},
{
path: '/three',
component: ChildThree,
},
]
},
父级页面
<p @click="$router.push('/child')">跳转到 child 页面</p>
childIndex 页面
<template>
<div>
<p @click="$router.push('/child')">跳转到 child 页面</p>
<p @click="$router.push('/child/one')">跳转到 child/one 页面</p>
<p @click="$router.push('/child/two')">跳转到 child/two 页面</p>
<p @click="$router.push('/three')">跳转到 /three 页面</p>
<br />
<p>下面是 router-view</p>
<router-view />
</div>
</template>
router 的一些方法
console.log('当前路由 currentRoute:', unref(router.currentRoute));
console.log('路由配置对象 options:', router.options);
console.log('路由记录的完整列表 getRoutes:', router.getRoutes());
console.log('是否存在指定名称的路由 hasRoute:', router.hasRoute('RouterTest'));
console.log(
'返回路由地址的标准化版本 resolve:',
router.resolve(unref(router.currentRoute).fullPath),
);

RouteRecordRaw
路由记录
path路由的路径redirect重定向路径children当前路由的嵌套路由。alias路由的别名。name路由记录的名称。beforeEnter在进入特定于此记录的守卫之前。注意如果记录有重定向属性,则beforeEnter无效。props允许将参数作为props传递给由router-view渲染的组件。当传递给一个多视图记录时,它应该是一个与组件具有相同键的对象,或者是一个应用于每个组件的布尔值。meta在路由上附加自定义数据。
RouteLocationNormalized(对应 route)
没有任何重定向记录的路由地址。在导航守卫中,to 和 from 属于这种类型。
fullPath返回完整的URL,编码与路由地址有关。包括path、query和hash。hash已解码URL的hash部分。总是以 #开头。如果URL中没有hash,则为空字符串。query从URL的search部分提取的已解码查询参数的字典。matched与给定路由地址匹配的标准化的路由记录数组。meta附加到从父级到子级合并(非递归)的所有匹配记录的任意数据。name路由记录的名称。如果什么都没提供,则为 undefined。params从path中提取的已解码参数字典。path编码 URL 的 pathname 部分,与路由地址有关。redirectedFrom在找到redirect配置或带有路由地址的名为next()的导航守卫时,我们最初尝试访问的路由地址,最后到达当前位置。如果没有重定向,则为undefined

还没有留言,来发表第一条吧~