1.在vue3项目中使用quill-editor

在控制台安装quill

1
npm install @vueup/vue-quill@latest --save

在项目中进行导入,这里演示局部导入,也可以进行全局导入

1
2
import { QuillEditor } from '@vueup/vue-quill'
import '@vueup/vue-quill/dist/vue-quill.snow.css'

使用数据

1
2
3
4
5
6
<quill-editor
theme="snow"
v-model:content="articleModel.content"
contentType="html"
>
</quill-editor>

2.在vue3项目中使用Echarts

通过npm安装

1
npm install echarts --save

引入

1
import * as echarts from 'echarts';
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
import {ref, onMounted} from "vue";

const sum = ref({
buildingNumber: '100',
residentsNumber: '888',
intelligentDevices: '300',
alarmNumber: '20'
})
//房屋数量情况
const buildingData = ref([
{value: 335, name: '毛坯房'},
{value: 310, name: '精装修'},
{value: 234, name: '简装修'},
{value: 135, name: '未卖出'},
])
//报修统计
const repairData = ref([
{value: 335, name: '正在处理'},
{value: 310, name: '已修复'},
{value: 234, name: '未修复'},
])
const paymentData = ref(
{
hasPay:[50, 80, 90, 400],
noPay:[10,20,30,50]
})


//导入图表
import * as echarts from 'echarts'
//钩子方法
onMounted(() => {
renderChart()
})
//表名
const building = ref()
const repairing = ref()
const paying = ref()


const renderChart = () => {
let buildingChart = echarts.init(building.value)
let repairChart = echarts.init(repairing.value)
let payChart = echarts.init(paying.value)
//图表自适应
window.addEventListener("resize", () => {
buildingChart.resize();
});
window.addEventListener("resize", () => {
repairChart.resize();
});
window.addEventListener("resize", () => {
payChart.resize();
});
buildingChart.setOption({
title:
{
text: '房屋分布',
left:
'left'
}
,
/*提示的样式*/
tooltip: {
trigger: 'item',
/* formatter:
'{a} <br/> {b} : {c} ({d}%)'*/
}
,
legend: {
orient: 'horizontal',
bottom: '10px',
data:
['毛坯房', '精装修', '简装修', '未卖出']
}
,
label: {
show: true,
formatter: '{b}:{c}'
},
series: [
{
name: '装修情况',
type: 'pie',
radius: '50%',
center: ['50%', '50%'],
data: buildingData.value,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 5,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
)
repairChart.setOption({
title:
{
text: '报修情况',
left:
'left'
}
,
tooltip: {
trigger: 'item',
/*formatter:
'{a} <br/> {b} : {c} ({d}%)'*/
},
label: {
show: true,
formatter:'{b}: {c}'
},
legend: {
orient: 'horizontal',
bottom: '10px',
data:
['正在处理', '已修复', '未修复']
}
,
series: [
{
name: '报修情况',
type: 'pie',
radius: '50%',
center: ['50%', '50%'],
data: repairData.value,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 5,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
)
payChart.setOption({
title:
{
text: '缴费情况',
left:
'left'
},
tooltip: {
trigger: 'axis'
},
legend: {
orient: 'horizontal',
bottom: '10px',
data: ['已缴','未缴']
},
label: {
show: true,
position: 'top'
},
xAxis: {
data: ['水费', '电费', '停车费', '物业费']
},
yAxis: {},
series: [
{
name: '已缴',
type: 'bar',
data: paymentData.value.hasPay
},{
name: '未缴',
type: 'bar',
data: paymentData.value.noPay
}
]
})


}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div style="display: flex;flex-direction: row;justify-content: space-between;margin-top: 4vh">

<div style="height:50vh;width:50vh ;margin-top: 20px;border-style: solid;border-color: #f5f1f1;">
<div ref="building" :style="{ float:'bottom', width: '100%', height: '100%' }"></div>
</div>
<div style="height:50vh;width:50vh ;margin-top: 20px;border-style: solid;border-color: #f5f1f1;">
<div ref="paying" :style="{ float:'bottom', width: '100%', height: '100%' }"></div>
</div>
<div style="height:50vh;width:50vh ;margin-top: 20px;border-style: solid;border-color: #f5f1f1;">
<div ref="repairing" :style="{ float:'bottom', width: '100%', height: '100%' }"></div>
</div>


</div>

3.vue项目的config配置文件

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
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
//配置文件的查找目录
publicPath:"/",
devServer: {
//编译完成后打开
open:true,
//配置运行端口
port: 8888,
//允许所有请求进入
allowedHosts: 'all',
//关闭浏览器网页的全屏报错
client: {
overlay: false,
},
//解决跨域问题
proxy: {
'/api': {
target: 'http://localhost:8080',//这里填入你要请求的接口的前缀
ws:true,//代理websocked
changeOrigin:true,//虚拟的站点需要更管origin
secure: true, //是否https接口
pathRewrite:{
'^/api':''//重写路径
}
}
}
}


})

4.拦截器

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
//定制请求的实例
//导入token状态
import { useTokenStore } from '@/stores/token.js';
//添加请求拦截器
//导入axios npm install axios
import axios from 'axios';
import router from '@/router';
import { ElMessage } from 'element-plus';
//定义一个变量,记录公共的前缀,baseURL
const baseURL = '/api';
const instance = axios.create({baseURL})
//添加请求拦截器
instance.interceptors.request.use(
(config)=>{
//在发送请求之前做什么
let tokenStore = useTokenStore()
//如果token中有值,在携带
if(tokenStore.token){
config.headers.Authorization=tokenStore.token
}
return config
},
(err)=>{
//如果请求错误做什么
Promise.reject(err)
}
)
//添加响应拦截器
instance.interceptors.response.use(
result=>{

if(result.data.code==0){
return result.data
}else{
ElMessage.error(result.data.message?result.data.message:'服务异常')
}

},
err=>{
if(err.response.status===401){
ElMessage.error('请先登录!')
router.push('/login')
}else{
ElMessage.error('服务异常');
}
return Promise.reject(err);//异步的状态转化成失败的状态
}
)

export default instance;

5.存储token

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
import {defineStore} from 'pinia'
import {ref} from 'vue'



export const useTokenStore = defineStore('token',()=>{
//1.定义描述token
const token =ref('')

//2.定义修改token的方法
const setToken = (newToken)=>{
token.value = newToken
}

//3.定义移除token的方法
const removeToken = ()=>{
token.value=''
}
return {
token,setToken,removeToken
}
}
,
//参数持久化
{
persist:true
}
)

6.安装element-ui

1
npm install element-plus --save
1
2
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
1
app.use(ElementPlus)

7.检测屏幕滚动

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
import { ref, onMounted, watch } from "vue";
//点击箭头,滑动页面
const scrollTop = ref('')
const topsrcoll = ref(false)//上移样式成立
onMounted(() => {
window.addEventListener('scroll', handleScroll)
})
//监听页面滚动
const handleScroll = () => {
scrollTop.value = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
}
// 监听top值的变化
watch(scrollTop, (newValue, oldValue) => {
// 等新值大于100的时候再做变化(优化一下)
console.log(newValue, oldValue)
// 等新值大于100的时候再做变化(优化一下)
if (newValue > 100) {
if (newValue > oldValue) {
console.log('向下滚动')
topsrcoll.value = true
} else {
console.log('向上滚动')
topsrcoll.value = false
}
}
})
1
2
3
<h1 v-if=topsrcoll>
dfgiusgdefisde
</h1>

8.在项目中使用scss

安装sass

1
npm i sass

安装loader

1
npm i sass-loader

使用

1
<style lang="scss" scoped>

9.安装路由

1
npm install vue-router@4

1.创建路由器,并导出

在src/router目录下,定义一个js文件,起名为index.js。这样名字的js文件在导入时,可以不写文件名,只要定位到文件所在的文件夹即可,使用起来很方便

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//导入vue-router
//createWebHistory一般使用history不适用hashhistory
import { createRouter, createWebHistory } from 'vue-router'
//导入组件
import LoginVue from '@/views/Login.vue'
import LayoutVue from '@/views/Layout.vue'

//定义路由关系
const routes = [
{ path: '/login', component: LoginVue },
{ path: '/', component: LayoutVue }
]

//创建路由器
const router = createRouter({
history: createWebHistory(),
routes: routes
});

export default router

2.在vue应用实例中使用router

在main.js中导入创建应用实力的js文件,并调用实例的use方法使用路由器

1
2
3
import router from '@/router'

app.use(router)

3.定义展示路由组件的地方

在App.vue文件的template标签中,定义router-view标签

1
2
3
<template>
<router-view></router-view>
</template>

将来不管根据路由匹配到的组件内容,会在router-view标签内进行展示

4.路由API

在登录成功后,需要通过代码的方式将页面切换到首页,此时就需要调用路由器相关的API

获取路由器

1
2
import { useRouter } from 'vue-router'
const router = useRouter();

调用API

1
router.push('/')

5.子路由

在咱们的主页面中,当用户点击左侧的菜单时,右侧主区域的内容需要发生变化,将来每切换一个菜单,右侧需要加载对应组件的内容进行展示,像这样的场景咱们也需要使用路由来完成

由于这些组件都需要在Layout.vue中展示, 而Layout.vue本身已经参与了路由,因此我们需要在Layout.vue中通过子路由的方式来完成组件的切换

5.1提供菜单对应的组件

可以复制资料中的文件,也可以自己创建:

  • ArticleCategory.vue
  • ArticleManage.vue
  • UserInfo.vue
  • UserAvatar.vue
  • UserResetPassword.vue

5.2配置子路由

在src/router/index.js中配置子路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//定义路由关系
const routes = [
{ path: '/login', component: LoginVue },
{
path: '/',
component: LayoutVue,
//重定向,默认访问此界面
redirect: '/article/manage',
//子路由
children: [
{ path: '/article/category', component: ArticleCategoryVue },
{ path: '/article/manage', component: ArticleManageVue },
{ path: '/user/info', component: UserInfoVue },
{ path: '/user/avatar', component: UserAvatarVUe },
{ path: '/user/password', component: UserResetPasswordVue },
]
}
]

5.3 在Layout.vue组件的右侧中间区域,添加router-view标签

1
2
3
4
5
6
<!-- 中间区域 -->
<el-main>
<div style="width: 1290px; height: 570px;border: 1px solid red;">
<router-view></router-view>
</div>
</el-main>

5.4 菜单项设置点击后跳转的路由路径

el-menu-item 标签的index属性可以设置点击后的路由路径

1
2
3
4
5
6
<el-menu-item index="/article/category">
<el-icon>
<Management />
</el-icon>
<span>文章分类</span>
</el-menu-item>

10.设置滚动条

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
.box {
height: 100px;
overflow: auto;
border: 1px solid red;
}
// 滚动条
.box::-webkit-scrollbar {
/*滚动条整体样式*/
width: 10px; /*高宽分别对应横竖滚动条的尺寸*/
height: 1px;
}
.box::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
border-radius: 10px;
height: 20px;
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
// background: #E5E5E5;
background: red;
}
.box::-webkit-scrollbar-track {
/*滚动条里面轨道*/
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
border-radius: 10px;
background: #ffffff;
}

11导航栏吸顶效应

1.安装vueuse

1
npm i @vueuse/core

使用vueuse

监听y轴的变化

1
import { useScroll } from '@vueuse/core'
1
const { y } = useScroll(window)

使用时监听y,当前项目中,当y大于78px时,自动展示

1
2
3
4
5
<div class="app-header-sticky" :class="{ show: y > 78 }">
<div class="container">

</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.app-header-sticky {
width: 100%;
height: 80px;
position: fixed;
left: 0;
top: 0;
z-index: 999;
background-color: #fff;
border-bottom: 1px solid #e4e4e4;
// 此处为关键样式!!!
// 状态一:往上平移自身高度 + 完全透明
transform: translateY(-100%);
opacity: 0;

// 状态二:移除平移 + 完全不透明
&.show {
transition: all 0.3s linear;
transform: none;
opacity: 1;
}
}

12.prop的使用

手册

在vue3语法糖的使用中,定义数据

1
2
3
4
5
6
7
8
9
10
11
// 定义props
defineProps({
// 主标题
title: {
type: String
},
// 副标题
subTitle: {
type: String
}
})

1
2
3
4
defineProps({
title: String,
subTitle:string,
})

在父组件中定义数据

1
<HomePanel title="新鲜好物" sub-title="新鲜出炉 品质靠谱">

定义一个object类型的数据

1
2
3
4
5
6
defineProps({
goods: {
type: Object,
default: () => { }
}
})

绑定数据

1
<GoodsItem v-for="goods in goodList" :goods="goods" :key="goods.id" />

13.懒加载

在vueuse组件中提供了懒加载指令

可以在main.js中直接使用,但是一般在main.js中不写逻辑语句,因此将其封装在插件文件夹中,将这个插件在main.js中导入,并且注册为全局组件,可读性较高,

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
// 定义懒加载插件
import { useIntersectionObserver } from '@vueuse/core'

export const lazyPlugin = {
install (app) {
// 懒加载指令逻辑
app.directive('img-lazy', {
mounted (el, binding) {
// el: 指令绑定的那个元素 img
// binding: binding.value 指令等于号后面绑定的表达式的值 图片url
console.log(el, binding.value)
//从中获得stop指令
const { stop } = useIntersectionObserver(
el,
([{ isIntersecting }]) => {
console.log(isIntersecting)
if (isIntersecting) {
// 进入视口区域
el.src = binding.value
//当加载完成后,停止监听,减少内存消耗
stop()
}
},
)
}
})
}
}

在main.js中注册

1
2
3
// 引入懒加载指令插件并且注册
import { lazyPlugin } from '@/directives'
app.use(lazyPlugin)
1
<img v-img-lazy="cate.picture" />

14.路由激活时一直显示样式

1
<router-link to="/home" class="menu-home" active-class="active">首页</router-link>
1
2
3
4
.active {
color: $xtxColor;
border-bottom: 1px solid $xtxColor;
}

15无限滚动

1
2
3
<ul v-infinite-scroll="load" class="infinite-list" style="overflow: auto" :infinite-scroll-disabled="disabled">

</ul>
1
2
3
4
5
6
7
8
9
10
11
12
13
// 加载更多
const disabled = ref(false)
const load = async () => {
console.log('加载更多数据咯')
// 获取下一页的数据
reqData.value.page++
const res = await getSubCategoryAPI(reqData.value)
goodList.value = [...goodList.value, ...res.result.items]
// 加载完毕 停止监听
if (res.result.items.length === 0) {
disabled.value = true
}
}

16在切换路由的时候添加动画

1
2
3
4
5
6
7
8
9
10
11
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in">
<keep-alive>

<component :is="Component" />


</keep-alive>
</transition>

</router-view>

且在根项目的时候加入div标签进行包裹

1
2
3
4
5
6
7
8
9
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}

.fade-enter-from,
.fade-leave-to {
opacity: 0;
}

17.在浏览器中使用webscoket

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
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket Demo</title>
</head>
<body>
<input id="text" type="text" />
<button onclick="send()">发送消息</button>
<button onclick="closeWebSocket()">关闭连接</button>
<div id="message">
</div>
</body>
<script type="text/javascript">
var websocket = null;
var clientId = Math.random().toString(36).substr(2);

//判断当前浏览器是否支持WebSocket
if('WebSocket' in window){
//连接WebSocket节点
websocket = new WebSocket("ws://localhost:8080/ws/"+clientId);
}
else{
alert('Not support websocket')
}

//连接发生错误的回调方法
websocket.onerror = function(){
setMessageInnerHTML("error");
};

//连接成功建立的回调方法
websocket.onopen = function(){
setMessageInnerHTML("连接成功");
}

//接收到消息的回调方法
websocket.onmessage = function(event){
setMessageInnerHTML(event.data);
}

//连接关闭的回调方法
websocket.onclose = function(){
setMessageInnerHTML("close");
}

//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function(){
websocket.close();
}

//将消息显示在网页上
function setMessageInnerHTML(innerHTML){
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}

//发送消息
function send(){
var message = document.getElementById('text').value;
websocket.send(message);
}

//关闭连接
function closeWebSocket() {
websocket.close();
}
</script>
</html>

18双token的使用

在前端的中,token过期会严重影响用户的体验,所以在常用用户登陆的时候,让token进行无感刷新显得非常重要,因此我们定义了两个token,一个refreshtoken和acesstoken,当accesstoken失效的时候,refreshtoken的有效期还在时代时候,可以获得一个新的ccesstoken。以vue3项目为演示

首先创建一个vue3项目,在项目中安装axios,封装一个request请求

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
//添加请求拦截器
//导入axios npm install axios
import axios from 'axios';



sessionStorage.getItem("refreshToken");
//定义一个变量,记录公共的前缀,baseURL
const baseURL = '/api';
const instance = axios.create({
baseURL
})
//添加请求拦截器
instance.interceptors.request.use(
(config) => {
return config
},
(err) => {
//如果请求错误做什么
Promise.reject(err)
}
)
//添加响应拦截器
instance.interceptors.response.use(
(response) => {
//对响应数据做点什么
//console.log(response)
if (response.data.code == 0) {
return response.data
} else {
console.log(response.data.msg)
}
},

(err) => {
return Promise.reject(err); //异步的状态转化成失败的状态
}
)

export default instance;

这是一个基本的axios请求,并且注册了拦截器,要让请求到达后端,我们还需要设置以下跨域问题,可以在前端设置,也可以在后端设置,这里就直接在前端进行配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 devServer: {
//允许所有请求进入
allowedHosts: 'all',
//关闭浏览器网页的全屏报错
client: {
overlay: false,
},
//解决跨域问题
proxy: {
'/api': {
target: 'http://localhost:8080',//这里填入你要请求的接口的前缀
ws:true,//代理websocked
changeOrigin:true,//虚拟的站点需要更管origin
secure: true, //是否https接口
pathRewrite:{
'^/api':''//重写路径
}
}
}
}

这样一个基本的前端项目就搭建完成,现在来创建两个按钮,用来发送请求

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
<template>
<button @click="handleClick">
按钮
</button>
<button @click="handleClick1">
发送请求
</button>
<div id="message">

</div>
</template>

<script setup>
import { ref } from 'vue';
import request from './request';
const accessToken = ref("");
const refreshToken = ref("");
const handleClick = async () => {
const message = await request.get('/get')
console.log("获得token");
accessToken.value = message.data.accessToken;
refreshToken.value = message.data.refreshToken;
//将数据保存在浏览器缓存中,关闭浏览器后消失,也可以在存储在其他容器中,这里演示,就直接存储在本地
sessionStorage.setItem("accessToken", accessToken.value);
sessionStorage.setItem("refreshToken", refreshToken.value);
}


const handleClick1 = async () => {
console.log("发起请求")
const message = await request.get('/text')
console.log("请求返回结果")
console.log(message);

}
</script>

<style></style>

其中按钮1是为了获得token并且将token进行存储按钮2是为了对受保护的请求发起请求,这个请求只有带有有效的token时才会返回数据,为了自动带上token,我们需要对请求拦截器进行一定的修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//添加请求拦截器
instance.interceptors.request.use(
(config) => {
const accessToken = sessionStorage.getItem("accessToken");
const refreshToken = sessionStorage.getItem("refreshToken");
//console.log(accessToken, refreshToken);
config.headers.accessToken = accessToken
config.headers.refreshToken = refreshToken
return config
},
(err) => {
//如果请求错误做什么
Promise.reject(err)
}
)

这段代码的意思是,从本地获得存储的token,再将其放入请求头中,

当accesstoken失效的时候,后端会给我们返回一个401未授权,我们需要在响应拦截器中拦截这个请求

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
//添加响应拦截器
instance.interceptors.response.use(
(response) => {
//对响应数据做点什么
//console.log(response)
if (response.data.code == 0) {
return response.data
} else {
console.log(response.data.msg)
}
},

async (err) => {
if (err.response.status === 401 && err.response.headers.isoverdue == "true") {
console.log('token失效')
} else if (err.response.status === 401 && !err.response.headers.isoverdue) {
console.log('重新请求')
const result = await instance.get("/refresh");
const accessToken = result.data.accessToken;
const refreshToken = result.data.refreshToken;
sessionStorage.removeItem("accessToken");
sessionStorage.removeItem("refreshToken");
sessionStorage.setItem("accessToken", accessToken);
sessionStorage.setItem("refreshToken", refreshToken);
const data = await instance.request(err.config)
return data
} else {
console.log(err.response.data.msg)
}
return Promise.reject(err); //异步的状态转化成失败的状态
}
)

后端请求回来时,如果refreshtoken也过期,会在请求头中添加isoverdue,当其存在时,说明refreshtoken已经过期,这时候,我们便需要重新登陆

不然我们需要重新请求获得新的token,我们拦截未登录的请求,并且向后端的刷新接口来刷新我们的token,将获得的token重新存入本地,通过instance.request(err.config)重新发送请求,这样我们便完成了双token

19.使用js监听浏览器窗口大小

一直检测时会导致浏览器性能下降,所有要限制他检测频率,进行防抖

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

// 监听窗口大小变化
const getWindowInfo = () => {
const windowInfo = {
width: window.innerWidth,
hight: window.innerHeight
}
if (windowInfo.width < 780) {
isCollapse.value = true;
} else {
isCollapse.value = false;
}
};
const debounce = (fn, delay) => {
let timer;
return function() {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn();
}, delay);
}
};
const cancalDebounce = debounce(getWindowInfo, 500);

window.addEventListener('resize', cancalDebounce);