持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情 (opens new window)
相信大家也知道大部分的网页版引应用,“不登录就不给看!”,于是,我也给自己的项目添加了这个小细节。如何实现呢?当然是使用路由守卫啦。
正如其名,导航守卫就是通过跳转或取消的方式守卫导航。这里使用的vue-router提供的导航守卫。
# 没有守卫时
以vue3为例,使用脚手架搭建项目, 命令代码:npm init vue@latest。选择所需的插件,其中因为方便举例,这里都是会话缓存(用到时再解释),没有使用其他状态管理器,如vuex或者pinia...。需要安装vue-router,接下来的改变都是以最初始的页面为基础(接下来就同意称之为基础页面),进行添加路由守卫。
登录页面和首页页面:
//登录页面 views/LoginView.vue
<template>
<div class="login">
<h1>This is an login page</h1>
用户名:<input type="text" v-model="formState.username" />
<div class="btn" style="margin-top: 20px;">
<button @click="onFinish">登录</button>
</div>
</div>
</template>
//登录页面 js代码
<script>
//...引入defineComponent
export default defineComponent({
setup() {
const formState = reactive({
username: ''
})
const onFinish = async () => {
console.log('success', formState.username)
}
return {
formState,
onFinish
}
}
})
</script>
//首页页面 /views/HomeView.vue
<template>
<div class="home">
<h1>This is an home page</h1>
</div>
</template>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
配置路由:
//路由配置 router/index.js
//...一些引入
const router = createRouter({
history: createWebHistory(
import.meta.env.BASE_URL),
routes: [{
path: '/',
redirect: '/home'
},
{
path: '/home',
name: 'home',
component: () => import('../views/HomeView.vue')
},
{
path: '/login',
name: 'login',
component: () => import('../views/LoginView.vue')
}
]
})
export default router
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
最开始其实就是准备两个vue3脚手架搭建好两个页面,登录页面和首页页面,然后配置路由即可。 看看效果:
# 一、全局路由守卫
# 知识基础
全局前置守卫
可以使用router.beforeEach()注册一个全局前置守卫。当一个导航触发时,全局前置守卫按照创建顺序调用。此时,导航在所有守卫resolve完之前一直处于等待之中。
const router = createRouter({ ... }) router.beforeEach((to, from, next) => {//路由跳转前 if(...){//不存在token //回到登录页面 return {name:'login'} }else{ next()//只有调用next(),才能成功跳转;否则不成功 } })
1
2
3
4
5
6
7
8
9参数:
(1)to:即将要进入的目标
(2)from:当前导航正要离开的路由
(3)next:去到下一个导航守卫调用。确保next在任何给定的导航守卫中都被严格调用一次。
全局解析守卫
使用router.beforeResolve()注册一个全局解析守卫。与全局前置守卫类似,在每次导航时触发,但是在确保导航被触发之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被正确调用。
const router = createRouter({ ... }) router.beforeResolve((to, from, next) => { //成功调到目的地页面之前 console.log(to, from); next(); })
1
2
3
4
5全局后置钩子
使用router.afterEach()注册一个全局后置钩子,与守卫不同,它不接受next第三个参数,函数也不会改变导航本身。
const router = createRouter({ ... }) router.afterEach((to, from) => { console.log(to, from); if (!sessionStorage.getItem('age') && to.name !== 'LoginView') { router.push('LoginView');//以这种方式强制回到登录页面 } })
1
2
3
4
5
6
7
# 路由守卫
浏览器缓存:
本地缓存
LocalStorage.setItem(key,value) 设置本地缓存
LocalStorage.setItem(key) 获取本地缓存
永久性缓存,可手动清除缓存。
会话缓存
SessionStorage.setItem(key,value) 设置会话缓存
SessionStorage.setItem(key) 获取会话缓存
暂时性缓存,结束会话即缓存清除,可手动清除缓存。
在没有做路由守卫页面的基础上,在登录页面增加做缓存的功能:
//...登录页面的html代码
<script>
//...一些引入
//...其他代码(标准格式)
const onFinish = async () => {
console.log('success', formState.username)
//这里是新添加的做缓存功能代码
// 假设拿到后端数据,接下来做缓存,比如在xxxx天后登录失效
if (formState.username) {
//设置会话缓存
sessionStorage.setItem('username', formState.username)
//路由跳转
router.push('/home')
} else {
alert('用户名为空')
}
}
//...其他代码(标准格式)
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
接下来,在全局设置路由守卫,即在搭建的基础之上,添加路由守卫代码。
//全局路由守卫:main.js
//...一些引入
// 全局路由守卫----全局前置守卫
router.beforeEach((to, from, next) => { //路由跳转前
// console.log(to, from);
// 判断是否在缓存,在的话允许访问首页;否则,跳回登录页面
if (!sessionStorage.getItem('username') && to.name !== 'login') {//不存在
return next({//去登录页面
name: 'login'
})
} else {//存在,允许去你想要的页面
next(); //只有调用next(),才能成功跳转;否则不成功
}
})
//...一些use
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
来看看守卫的结果吧:
# 二、路由独享守卫
# 知识基础
路由独享的守卫
const routes = [ { path: '/users/:id', component: UserDetails, beforeEnter: (to, from,next) => { // return false if(...){//未登录或者为授权 //强制回到登录页面,或者retun false拒绝导航 next({ name: 'login' }) }else{ next()//去到下一个导航守卫 } }, }, ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17路由独享的守卫只有在进入路由时才会触发,不会再路径改变参数(params)、query、hash等时触发。它只是只有在从一个不同的路由导航时,才会被触发。也可以将函数数组传给beforeEnter,在为不同的路由重用守卫时大有作为。
# 路由守卫
在基础页面上,在你想要守卫的路由组件下面增加路由守卫,如下
//路由配置 router/index.js
{
path: '/home',
name: 'home',
component: () => import('../views/HomeView.vue'),
//这里是新添加的路由守卫代码
// 路由守卫---
beforeEnter: (to, from, next) => {
if (!sessionStorage.getItem('username')) {
alert('请先登录');
next({
name: 'login'
})
} else {
next();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
看看路由守卫结果:
# 三、组件内的守卫
# 知识基础
beforeRouteEnter
beforeRouteEnter()一般在渲染该组件的对应路由被验证前调用,但是不能获取组件实例的this,因为当前守卫执行时,组件实例还没被创建。
beforeRouteEnter(to, from, next) { // 进入当前组件 if (...) {//没有满足要求 return next({ name: 'login' }) } else { next(); //只有调用next(),才能成功跳转;否则不成功 } }
1
2
3
4
5
6
7
8
9
10beforeRouteUpdate
beforeRouteUpdate()一般在当前路由改变,但是该组件被复用时调用,如同一个路径下携带参数不一样的情况下。因为路径相同会渲染相同的组件,因此组件实例会被复用,而钩子函数就会在这个时候会被调用。在这种情况下,组件已经挂载就绪,导航守卫可以访问组件实例的this。正是因为可以使用this,所以不支持next的调用。
beforeRouteUpdate(to, from) { console.log(this) this.name=to.params.name },
1
2
3
4beforeRouteLeave
beforeRouteLeave(),离开守卫,通常用来预防用户还在未保存修改之前就突然去其他页面。顾名思义,一般在导航离开渲染组件的对应路由时调用。也可以访问组件实例的this,同时也没有next可以调用。
beforeRouteLeave(to, from) { console.log(this) return false//可取消守卫 }
1
2
3
4
# 路由守卫
组件内的守卫,那么只需要在页面进行路由守卫即可,在基础1页面上,在需要守卫的页面上添加路由守卫:
//home页面js代码
<script>
import {
defineComponent
} from "vue"
export default defineComponent({
beforeRouteEnter(to, from, next) {
// 进入当前组件
if (!sessionStorage.getItem('username') && to.name !== 'login') {
return next({
name: 'login'
})
} else {
console.log(1233333333333);
next(); //只有调用next(),才能成功跳转;否则不成功
}
}
})
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
看看守卫结果:
# 总结
全局路由守卫三个钩子函数,全局前置守卫(beforeEach),全局解析守卫(beforeResolve)和全局后置守卫(afterEach),其中全局后置守卫(afterEach)没有next可以调用,即不能传递第三个回调参数next;
路由独享的守卫(beforeEnter),只有在进入路由时才会触发;
组件内的路由守卫,也有三个api可以使用,分别是beforeRouteEnter,beforeRouteUpdate,beforeRouteLeave,其中因为beforeRouteEnter调用的时候,组件实例还没有被创建,所以只有beforeRouteEnter可以传递第三个回调参数next,因为beforeRouteUpdate,beforeRouteLeave被调用的时候,组件实例已经被挂载就绪,可以访问组件实例的this,所以没必要next的存在了。