全局前置守卫

每当有路由进行跳转前,都会执行全局前置守卫的逻辑

/router/index.ts:

1
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
37
38
39
40
import { createRouter, createWebHistory } from "vue-router"
import type { RouteRecordRaw } from "vue-router"

const routes: Array<RouteRecordRaw> = [
{
path: "/",
component: ()=> import("@/components/Login.vue")
},
{
path: "/index",
component: ()=> import("@/components/Index.vue")
}
]

const router = createRouter({
// 路由的模式
history: createWebHistory(),
// 路由信息
routes
})

// 定义一个路径白名单
const whiteList = ["/"]

// 路由全局前置守卫
// to:要去哪 from:从哪来 next:执行跳转操作
router.beforeEach((to,from,next)=> {
// 使白名单内路径不会被全局前置守卫拦截,同时检测是否有登录token
if(whiteList.includes(to.path) || localStorage.getItem("token")) {
// 不使用next()就不能跳转
next()
} else {
// 给next()传参,会跳转到指定路径
next("/")
// 不跳转,如果改变浏览器url,则会跳转至from的地址
// next(false)
}
})

export default router

Login.vue:

1
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<template>
<div class="login">
<el-card class="box-card">
<el-form ref="form" :model="formInline" :rules="rules" class="demo-form-inline">
<el-form-item prop="user" label="账号">
<el-input v-model="formInline.user" placeholder="请输入账号" clearable />
</el-form-item>
<el-form-item prop="password" label="密码">
<el-input type="password" v-model="formInline.password" placeholder="请输入密码"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">登录</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>

<script setup lang="ts">
import { ref, reactive } from 'vue'
import { useRouter } from "vue-router"
import { ElMessage } from "element-plus"
import type { FormItemRule, FormInstance } from "element-plus"

const router = useRouter()

type Form = {
user: string
password: string
}

type Rules = {
// 遍历Form类型的key
[K in keyof Form]?: Array<FormItemRule>
}

const formInline = reactive<Form>({
user: '',
password: '',
})

const form = ref<FormInstance>()
const rules: Rules = {
user: [
{
// 必填
required: true,
// 填写不合规时的提示信息
message: "必须输入账号",
// 类型
type: "string",
// 触发的条件
// trigger: "change"
}
],
password: [
{
required: true,
message: "必须输入密码",
type: "string",
}
]
}

const onSubmit = () => {
form.value?.validate((validate)=> {
console.log(validate)
// 验证是否通过
if(validate) {
router.push("/index")
localStorage.setItem("token", "114514")
} else {
ElMessage.error("请检查输入")
}
})
}
</script>

<style scoped lang="scss">
.login {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
</style>

Index.vue:

1
2
3
4
5
<template>
<div>
登陆成功
</div>
</template>

全局后置守卫

每当有路由进行跳转后,都会执行全局后置守卫的逻辑

后置守卫相比前置守卫,没有next函数,也不改变导航

/router/index.ts:

1
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
37
38
39
40
41
42
43
44
45
46
47
import { createRouter, createWebHistory } from "vue-router"
import type { RouteRecordRaw } from "vue-router"
import { createVNode, render } from "vue"
import LoadingBar from "@/components/LoadingBar.vue"

// 将进度条转成虚拟DOM
const Vnode = createVNode(LoadingBar)
// 挂载进度条
render(Vnode, document.body)

const routes: Array<RouteRecordRaw> = [
{
path: "/",
component: ()=> import("@/components/Login.vue")
},
{
path: "/index",
component: ()=> import("@/components/Index.vue")
}
]

const router = createRouter({
// 路由的模式
history: createWebHistory(),
// 路由信息
routes
})

// 定义一个路径白名单
const whiteList = ["/"]

router.beforeEach((to,from,next)=> {
Vnode.component?.exposed?.startLoading()
if(whiteList.includes(to.path) || localStorage.getItem("token")) {
next()
} else {
next("/")
}
})

// 路由全局后置守卫
// to:要去哪 from:从哪来
router.afterEach((to,from)=> {
Vnode.component?.exposed?.endLoading()
})

export default router

LoadingBar.vue:

1
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<template>
<div class="wraps">
<div ref="bar" class="bar"></div>
</div>
</template>

<script setup lang="ts">
import { ref } from "vue"

let bar = ref<HTMLElement>()
let speed = ref<number>(1)
let timer = ref<number>(0)

const startLoading = () => {
let dom = bar.value as HTMLElement
speed.value = 1
// requestAnimationFrame动画帧 回调函数只会执行一次
timer.value = window.requestAnimationFrame(function fn() {
console.log(speed.value, 1)
if (speed.value < 90) {
speed.value++
dom.style.width = speed.value + "%"
// 递归requestAnimationFrame 执行动画
timer.value = window.requestAnimationFrame(fn)
} else {
speed.value = 1
window.cancelAnimationFrame(timer.value)
}
})
}

const endLoading = () => {
let dom = bar.value as HTMLElement
window.setTimeout(() => {
window.requestAnimationFrame(() => {
speed.value = 100
dom.style.width = speed.value + "%"
})
}, 200)

}

defineExpose({
startLoading,
endLoading
})
</script>

<style scoped lang="scss">
.wraps {
position: fixed;
top: 0;
width: 100%;
height: 2px;

.bar {
height: inherit;
width: 0;
background-color: blue;
}
}
</style>