> 技术文档 > 微服务的编程测评系统6-管理员登录前端-前端路由优化

微服务的编程测评系统6-管理员登录前端-前端路由优化


提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 1. 管理员登录前端
    • 1.1 测试
    • 1.2 同源策略
    • 1.3 修改前端端口号
    • 1.4 跨域问题
    • 1.5 接收响应数据
    • 1.6 js-cookie
    • 1.7 错误消息提示
    • 1.8 优化
    • 1.9 响应拦截器
    • 1.10 @用法
  • 2. 后台管理-布局
    • 2.1 点击跳转不同页面
  • 3. 获取当前用户信息
    • 3.1 数据库修改
    • 3.2 设计
    • 3.3 开发后端
    • 3.4 开发前端
  • 4. 退出登录
    • 4.1 业务分析
    • 4.2 后端开发
    • 4.3 前端开发
  • 5. 前端路由优化
    • 5.1 重定向
    • 5.2 全局前置守卫
    • 5.3 token过期处理
  • 总结

前言

1. 管理员登录前端

1.1 测试

微服务的编程测评系统6-管理员登录前端-前端路由优化
测试一下发现报了这个错
这个主要是因为我们配置的前置url没有含有http协议,所以浏览器就会自动加上静态资源的url

const service = axios.create({ baseURL: \"http://127.0.0.1:19090/system\", timeout: 1000,})

这样就Ok了
微服务的编程测评系统6-管理员登录前端-前端路由优化
但是又出了一个新的问题
这个就是跨域问题

1.2 同源策略

微服务的编程测评系统6-管理员登录前端-前端路由优化

1.3 修改前端端口号

在vite.config.js里面添加

 server: { port: 5555, }

这样就可以

微服务的编程测评系统6-管理员登录前端-前端路由优化

1.4 跨域问题

我们可以用一个代理服务器来处理
浏览器前端先请求同源的代理服务器,然后代理服务器把请求转发到后端

因为浏览器有同源策略的约束
但是代理服务器是没有同源策略的约束的

 server: { proxy: { \"/dev-api\": { target: \"http://127.0.0.1:19090/system\", rewrite: (p) => p.replace(/^\\/dev-api/, \"\"), }, }, },

还是在vite.config.js里面,这样设置就可以了
这个就是对代理规则的配置

然后修改request.js里面的前置url

const service = axios.create({ baseURL: \"/dev-api\", timeout: 1000,})

如果没有加协议的话,浏览器会把前端的url拼接到baseURL上
所以请求地址为
http://localhost:5173/dev-api/sysUser/login
这个不会发生跨域问题
这个是同源的
会报404,找不到吗
不会
因为配置了代理规则
代理规则就是前缀包含/dev-api的时候,就会把请求转发到http://127.0.0.1:19090/system

 rewrite: (p) => p.replace(/^\\/dev-api/, \"\"),

这个就是把/dev-api变为空的字符串
所以http://localhost:5173/dev-api/sysUser/login就会变为
http://127.0.0.1:19090/system/sysUser/login
先把http://localhost:5173替换为http://127.0.0.1:19090/system,然后去掉/dev-api,就OK了

微服务的编程测评系统6-管理员登录前端-前端路由优化
补充一下,axios可以自动转换JSON数据,所以那样写没有问题

测试一下也是没有问题的

1.5 接收响应数据

但是axios这个调用接口的过程是一个异步的过程
往往不那么好拿到返回结果
所以要用await去获取异步操作结果
但是对应的调用他的函数也要为async

async function loginFun() { const res = await loginService(userAccount.value, password.value); if(res.data.code === 1000){ console.log(\"登录成功:\" , res.data); }else{ console.log(res.data.msg) }}

async 表示这个函数要使用await,await表示调用这个方法接口的时候用异步的方式
console.log(“登录成功:” , res.data);这里,如果是 console.log(“登录成功:” +res.data);

那么res.data打印出的内容是object
如果要打印出类的详细数据的话,还是得用逗号隔开
微服务的编程测评系统6-管理员登录前端-前端路由优化
登录成功要跳转页面
我们用router的push方法就可以了

微服务的编程测评系统6-管理员登录前端-前端路由优化

 { path: \'/oj/system\', name: \'system\', component: () => import(\'../views/System.vue\') }

记得还要配置路由

import router from \'@/router\'router.push(\"/oj/system\")

这样就可以跳转了

1.6 js-cookie

登录成功以后要存储token
怎么存储呢
存储方式有很多种
比如cookie和local-storige
我们这里使用cookie存储
js-cookie就是来操作cookie的

npm install js-cookie

在utils下创建cookie.js

import Cookies from \"js-cookie\";const TokenKey = \"Admin-Oj-b-Token\";export function getToken() { return Cookies.get(TokenKey);}export function setToken(token) { return Cookies.set(TokenKey, token);}export function removeToken() { return Cookies.remove(TokenKey);}

这样就可以了

import { setToken } from \'@/utils/cookie\'setToken(res.data.data)

微服务的编程测评系统6-管理员登录前端-前端路由优化
在appication.cookies那里就可以看到我们设置的cookie了

1.7 错误消息提示

import { ElMessage } from \'element-plus\'ElMessage.error(res.data.msg)

1.8 优化

 <el-input v-model=\"password\" type=\"password\" show-password placeholder=\"请输入密码\" />

给按钮加上 type=“password”
就可以把密码隐藏起来了
show-password就是显示是不是显示小眼睛

1.9 响应拦截器

我们在request.js里面设置

service.interceptors.response.use( (res) => { // 未设置状态码则默认成功状态 const code = res.data.code; const msg = res.data.msg; if (code !== 1000) { ElMessage.error(msg); return Promise.reject(new Error(msg)); } else { return Promise.resolve(res.data); } }, (error) => { return Promise.reject(error); });

为什么要用响应拦截器呢
因为我们可以用响应拦截器对响应进行拦截,,可以直接返回后端返回的result数据
现在我们就在登录成功和失败的情况下分别测试一下

 const res = await loginService(userAccount.value, password.value); console.log(\"登录成功:\" , res.data);

微服务的编程测评系统6-管理员登录前端-前端路由优化
微服务的编程测评系统6-管理员登录前端-前端路由优化
我们可以看出登录成功返回的数据就是后端返回的result,没有进行分装了
这个就是Promise.resolve(res.data)的作用
而Promise.reject(new Error(msg))就是相当于返回一个异常了
直接报错了

 (error) => { return Promise.reject(error); }

这里是属于错误返回,其他的都是正常返回
我们控制台肯定不能这样打印的,因为这样打印就是相当于出错了
所以我们还要捕获异常
就和后端捕获异常是一样的写法

async function loginFun() { try{ const res = await loginService(userAccount.value, password.value); setToken(res.data.data) router.push(\"/oj/system\") console.log(\"登录成功:\" , res.data); }catch(err){ console.log(\"登录失败:\" , err); }}

这样就可以了

1.10 @用法

import { setToken } from \'@/utils/cookie\'

这里的@是什么意思呢

 resolve: { alias: { \'@\': fileURLToPath(new URL(\'./src\', import.meta.url)) }, },

其实在vite.config.js里面就配置过了
@就是./src,所以很方便使用
微服务的编程测评系统6-管理员登录前端-前端路由优化
相应的router这里的两点我们也可以改为@

微服务的编程测评系统6-管理员登录前端-前端路由优化

2. 后台管理-布局

创建一个布局的文件
Layout.vue

<template> <el-container class=\"layout-container\"> <el-header class=\"el-header\"> <el-dropdown> <span class=\"el-dropdown__box\"> <div> <strong>当前用户:</strong>超级管理员 </div> <el-icon> <ArrowDownBold /> </el-icon> <!-- <el-icon><Lock /></el-icon> --> </span> <template #dropdown> <el-dropdown-menu> <el-dropdown-item @click=\"logout\" :icon=\"SwitchButton\">退出登录</el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> </el-header> <el-main class=\"layout-bottom-box\"> <div class=\"left\"> <el-aside width=\"200px\" class=\"el-aside\"> <el-menu class=\"el-menu\" router> <el-menu-item index=\"/oj/layout/cuser\">  <el-icon> <Management />  </el-icon>  <span>用户管理</span> </el-menu-item> <el-menu-item index=\"/oj/layout/question\">  <el-icon> <Management />  </el-icon>  <span>题目管理</span> </el-menu-item> <el-menu-item index=\"/oj/layout/exam\">  <el-icon> <Management />  </el-icon>  <span>竞赛管理</span> </el-menu-item> </el-menu> </el-aside> </div> <div class=\"right\"> <RouterView /> </div> </el-main> </el-container></template><script setup>import { Management, ArrowDownBold, Lock, SwitchButton} from \'@element-plus/icons-vue\'</script><style lang=\"scss\" scoped>.layout-container { height: 100vh; background: #f7f7f7; .layout-bottom-box { display: flex; justify-content: space-between; height: calc(100vh - 100px); overflow: hidden; .left { margin-right: 20px; background: #fff; display: flex; :deep(.el-menu) { flex: 1; .el-menu-item.is-active { color: #32c5ff; } .el-menu-item:hover { background: #fff; color: #32c5ff; } } } .right { flex: 1; overflow-y: auto; background: #fff; padding: 20px; } } .el-aside { background-color: #fff; &__logo { height: 120px; // background: url(\'@/assets/logo.png\') no-repeat center / 120px auto; } .el-menu { border-right: none; } } .el-header { background-color: #fff; display: flex; align-items: center; justify-content: flex-end; height: 40px; .el-dropdown__box { display: flex; align-items: center; .el-icon { color: #4c4141; margin-left: 20px; } &:active, &:focus { outline: none; } } } .el-footer { display: flex; align-items: center; justify-content: center; font-size: 14px; color: #666; }}</style>

然后配置路由

 { path: \'/oj/layout\', name: \'layout\', component: () => import(\'@/views/Layout.vue\') }

微服务的编程测评系统6-管理员登录前端-前端路由优化
这样就可以了
现在分析一下
分成了三个部分
微服务的编程测评系统6-管理员登录前端-前端路由优化
我们用的是是这个container布局容器

 <el-container class=\"layout-container\"> <el-header class=\"el-header\"> </el-header> <el-main class=\"layout-bottom-box\"> <div class=\"left\"> <el-aside width=\"200px\" class=\"el-aside\">  </el-aside> </div> <div class=\"right\"> <RouterView /> </div> </el-main> </el-container>

这个就是布局
整体结构

dropdown是一个下拉菜单

微服务的编程测评系统6-管理员登录前端-前端路由优化
微服务的编程测评系统6-管理员登录前端-前端路由优化

ArrowDownBold是图标
微服务的编程测评系统6-管理员登录前端-前端路由优化
微服务的编程测评系统6-管理员登录前端-前端路由优化

import { Management, ArrowDownBold, Lock, SwitchButton} from \'@element-plus/icons-vue\'

但是使用图标要在js里面import

SwitchButton也是图标

el-aside我们用的是el-menu来写的

2.1 点击跳转不同页面

 <div class=\"right\"> <RouterView /> </div>

这里就是根据不同url,渲染不同页面,就是主要的页面内容
先创建对应的vue文件

微服务的编程测评系统6-管理员登录前端-前端路由优化
然后是配置router
微服务的编程测评系统6-管理员登录前端-前端路由优化
怎么实现点击切换不url呢

官网的menu组件有一个router属性
微服务的编程测评系统6-管理员登录前端-前端路由优化
这样就可以实现点击切换url了

首先先加上属性router
因为默认为false,不启用,加上就启动了

 <el-menu class=\"el-menu\" router>

在el-menu这里加上router属性,表示启动router
然后还不行,因为点击要跳转到哪里呢,
这下就要设置index了

微服务的编程测评系统6-管理员登录前端-前端路由优化
所以index设置为路径就可以了

 <el-menu-item index=\"/oj/question\">

这样设置就可以了
微服务的编程测评系统6-管理员登录前端-前端路由优化
但是点击跳转直接跳转到新的页面了,而不是在那个主页面展示
为什么会这样呢
因为我们配置的路径是/oj/question,与/oj/layout是同一级的
这个路由发生改变之后
会触发routerview,但是这个触发的routerview是app.vue那里的

 { path: \'/oj/layout\', name: \'layout\', component: () => import(\'@/views/Layout.vue\') }, { path: \'/oj/cuser\', name: \'cuser\', component: () => import(\'@/views/Cuser.vue\') },

这里的配置路径就是同一级的,所以改变路径的时候就是触发的同一个routerview,因为这两个的路径的配置是类似的,所以layout能触发那个routerview,为什么cuser不行呢
所以cuser也是用的app.vue的routerview
所以我们需要把cuser的路由配置到layout里面去
因为cuser是在layout内部进行的页面渲染
因为渲染的顺序就是先渲染layout,然后是点击在layout里面渲染cuser
所以路径的配置,就必须在layout里面进行配置
所以cuser的路由就是layout下的路由
路由提供了一个child的属性就可以配置了,这个是数组

 { path: \'/oj/layout\', name: \'layout\', component: () => import(\'@/views/Layout.vue\'), children: [ { path: \'/cuser\', name: \'cuser\', component: () => import(\'@/views/Cuser.vue\') }, { path: \'/exam\', name: \'exam\', component: () => import(\'@/views/Exam.vue\') }, { path: \'/question\', name: \'question\', component: () => import(\'@/views/Question.vue\') }, ] },

这样就可以了
其中cuser的路径就是/oj/layout/cuser
会自动加上父组件的路径的

 router.push(\"/oj/layout\")

然后登录成功的跳转也要改了

 <el-menu-item index=\"/oj/layout/cuser\">

然后这里也要改
微服务的编程测评系统6-管理员登录前端-前端路由优化

但是还是不行

 <div class=\"right\"> <RouterView /> </div>

这里的routerview是二级目录
这里跳转显示的是二级路由,而app.vue里面显示跳转的是一级路由
这里的情况是在一级小的页面里面有二级页面,所以对应也要在app.vuede的routerview里面在嵌套一个routerview
因为页面有嵌套关系
所以routerview也要有嵌套关系,路由配置也要有嵌套关系

 { path: \'/oj/layout\', name: \'layout\', component: () => import(\'@/views/Layout.vue\'), children: [ { path: \'cuser\', name: \'cuser\', component: () => import(\'@/views/Cuser.vue\') }, { path: \'exam\', name: \'exam\', component: () => import(\'@/views/Exam.vue\') }, { path: \'question\', name: \'question\', component: () => import(\'@/views/Question.vue\') }, ] },

注意这里的cuser的路径前面就不要加上/了,因为这样可能表示是以/cser开头的,是绝对路径,如果是二级路径,就最前面不要加/了
子路由 path 不加 /:路径会自动拼接父路由路径,保持嵌套关系(正确用法)。
子路由 path 加 /:路径被视为绝对路径,脱离父路由,成为独立的一级路由(不符合二级路由的设计意图)。

3. 获取当前用户信息

3.1 数据库修改

给数据库添加用户昵称字段

 nick_name varchar(20) not null comment \'昵称\',

要么重新创建数据库
要么用alter

alter table tb_sys_user add nick_name varchar(20) null after user_account ;
update tb_sys_user set nick_name = \'超级管理员\' where user_account = \'aaa\'
[HY000][1366] Incorrect string value: \'\\xE8\\xB6\\x85\\xE7\\xBA\\xA7...\' for column \'nick_name\' at row 1

在update的时候报错了,这个是因为不支持中文的原因
就是编码出问题
改一下配置文件就可以了

微服务的编程测评系统6-管理员登录前端-前端路由优化
找到etc/my.cnf
加上配置

character-set-server=utf8mb4collation-server = utf8mb4_general_ci

保存一下,然后重启容器生效

但是就算这样修改了执行还是不行,因为这个表提前就创建好了,编码已经确定了
所以不行
所以我们要重新创建一个表
但是就算创建一个新的表还是有编码问题
怎么回事呢
因为数据库的编码没有变
得创建一个新的库才可以
我们先用root用户创建新的库
所以说改了配置文件以后,删除以前的库才可以,或者创建新的库才可以生效
右键表的数据,然后生成sql,生成insertSQL就可以保存数据了
微服务的编程测评系统6-管理员登录前端-前端路由优化
这样就成功了

3.2 设计

微服务的编程测评系统6-管理员登录前端-前端路由优化

3.3 开发后端

 @GetMapping(\"/info\") public R<LoginUserVO> info(@RequestHeader(HttpConstants.AUTHENTICATIO) String token){ return sysUserService.info(token); }

我们的token直接从header里面获取就可以了,用的是RequestHeader注解

@Datapublic class LoginUserVO { private String nickName;}
@Datapublic class LoginUser { //存储在redis中的用户信息 private Integer identity; private String nickName;}

这里也要完善一下,存储到redis中的基本数据,还有数据库对应的类也要增加字段,记得还要修改对应的代码,登录存储基本用户数据的时候记得修改代码,存储昵称
微服务的编程测评系统6-管理员登录前端-前端路由优化

tokenService里面分装方法

 public LoginUser getLoginUser(String token, String secret ) { String userKey = getUserKey(token, secret); if(userKey == null){ return null; } String tokenKey = getTokenKey(userKey); return redisService.getCacheObject(tokenKey, LoginUser.class); } private String getTokenKey(String userKey) { return CacheConstants.LOGIN_TOKEN_KEY + userKey; } private String getUserKey(String token, String secret) { Claims claims; try { claims = JwtUtils.parseToken(token, secret); //获取令牌中信息 解析payload中信息 if (claims == null) { log.error(\"令牌已过期或验证不正确!\"); return null; } } catch (Exception e) { log.error(\"令牌已过期或验证不正确!e:\",e); return null; } return JwtUtils.getUserKey(claims); //获取jwt中的key }
 @Override public R<LoginUserVO> info(String token) { if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) { token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY); } LoginUser loginUser = tokenService.getLoginUser(token,secret); if(loginUser == null){ return R.fail(); } LoginUserVO loginUserVO = new LoginUserVO(); loginUserVO.setNickName(loginUser.getNickName()); return R.ok(loginUserVO); }

然后测试一下

del+key可以删除redis数据

微服务的编程测评系统6-管理员登录前端-前端路由优化
微服务的编程测评系统6-管理员登录前端-前端路由优化
这样就成功了
这个是根据登录设置的header自动进行查询的,不用传json
然后测试一下延长redis时间的接口也是没有问题的

3.4 开发前端

export function getUserInfoService(){ return service({ url: \'/sysUser/info\', method: \'get\' })}
import { reactive } from \'vue\';import { getUserInfoService } from \'@/apis/suser\';const loginUser = reactive({ nickName: \'\'})async function getUserInfo(){ const userInfo = await getUserInfoService(); loginUser.nickName = userInfo.data.nickName;}await getUserInfo();

然后再request.js里面定义请求拦截器

//请求拦截器service.interceptors.request.use( (config) => { if (getToken()) { config.headers[\"Authorization\"] = \"Bearer \" + getToken(); } return config; }, (error) => { console.log(error) Promise.reject(error); });

这个请求拦截器就是拦截每个给后端发起的请求,然后判断是否有token,有的话,就在请求头中加上token

 <div> <strong>当前用户:</strong>{{loginUser.nickName}} </div>

注意登录的前端接口写错了
改一下为这个样子

async function loginFun() { try{ const res = await loginService(userAccount.value, password.value); setToken(res.data) router.push(\"/oj/layout\") console.log(\"登录成功:\" , res.data); }catch(err){ console.log(\"登录失败:\" , err); }}

这样就可以了
微服务的编程测评系统6-管理员登录前端-前端路由优化

4. 退出登录

4.1 业务分析

就是让token不为空,然后解析一下,解析出来不能执行正常业务了。是可以进行解析的
所以点击退出登录,让redis中的数据不存在就可以了
微服务的编程测评系统6-管理员登录前端-前端路由优化
这样就可以避免多次登录,redis中的数据增多了
后端返回请求以后,如果是成功的,前端就清楚存储的token
所以就是后端清楚redis,前端清楚token
微服务的编程测评系统6-管理员登录前端-前端路由优化

4.2 后端开发

 @DeleteMapping(\"/logout\") @Operation(summary = \"退出登录\", description = \"退出登录\") public R<Void> logout(@RequestHeader(HttpConstants.AUTHENTICATION) String token){ log.info(\"退出登录...,token:{}\", token); return toR(sysUserService.logout(token)); }

因为退出登录后端会删除redis所以是DeleteMapping

 @Override public boolean logout(String token) { if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) { token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY); } return tokenService.deleteLoginUser(token,secret); }
 public boolean deleteLoginUser(String token, String secret) { String userKey = getUserKey(token, secret); if(userKey == null){ return false; } String tokenKey = getTokenKey(userKey); return redisService.deleteObject(tokenKey); }

然后测试一下

4.3 前端开发

点击退出登录的时候会有一个消息弹窗
我们用的就是elementplus的消息弹窗
微服务的编程测评系统6-管理员登录前端-前端路由优化
微服务的编程测评系统6-管理员登录前端-前端路由优化
观察一下我们可以发现
ElMessageBox.confirm 方法返回一个 Promise 对象。当用户点击确认按钮时,Promise 会进入 resolved 状态,此时会执行 .then() 中的回调函数;而当用户点击取消按钮或者关闭对话框时,Promise 会进入 rejected 状态,这时就会执行 .catch() 中的回调函数。
所以说用户点击取消按钮或者关闭对话框时,就是相当于抛出了一个异常,所以弹窗如果后面还有代码就不会执行了
而点击了确定按钮的话(其实点击什么都是返回Promise ),会返回一个 Promise 对象,返回这个对象是一个异步的过程,所以要await,不然异步的话,就去判断Promise 对象,可能会判断失误
所以说点击了确定按钮的话,就会执行弹窗后面的代码了

export function logoutService(){ return service({ url: \'/sysUser/logout\', method: \'delete\' })}

如果是函数抛出异常,也会结束后面代码执行

async function logout(){ await ElMessageBox.confirm( \'退出登录\', \'温馨提示\', { confirmButtonText: \'确认\', cancelButtonText: \'取消\', type: \'warning\', } ) await logoutService(); removeToken(); router.push(\'/oj/login\');}

因为logoutService抛出的异常我们可以直接输出错误,对于异常的情况没有什么好处理的,就什么都不干就可以了,所以我们不用try和catch

5. 前端路由优化

5.1 重定向

这个的问题是什么呢
微服务的编程测评系统6-管理员登录前端-前端路由优化
就是我们点击这个地址不用直接到login,而是要手动输入地址才可以了
什么做到点击这个http://localhost:5173/,就可以自动跳转到login呢,这个就要使用重定向了

 { path: \'/\', redirect: \'/oj/login\' },

这样配置就可以了

5.2 全局前置守卫

我们要求
未登录要求不管点击哪个页面都要跳回登录页面
登录过后,未过期,点回login自动跳转功能页面
登录过后,点击login,就直接不用登录就可以使用功能了

就是要在路由跳转之前进行判断处理
谁来进行页面跳转呢,就是router,router在哪里呢,就是在index.js里面配置的,所以对router进行配置即可,就是对页面跳转之前进行的配置

router.beforeEach((to, from, next) => { if (getToken()) { //已经登陆过 /* has token*/ if (to.path === \'/oj/login\') { next({ path: \'/oj/layout/question\' }) } else { next() } } else { if (to.path !== \'/oj/login\') { next({path:\'/oj/login\'}) } else { next() } }})

这样就可以了
to是目的路由
from是源路由
next是真正的目的路由是去哪里
next()就是和to一样的

这样我们在没有登录的情况下输入http://localhost:5173/oj/layout
就会自动跳转为http://localhost:5173/oj/login
登录下输入http://localhost:5173/oj/login
就会自动变为http://localhost:5173/oj/layout/question
但是如果登录状态过期呢
这个也是和没有登录是一样的,怎么判断呢,前端是无法判断token是否过期的

5.3 token过期处理

 boolean isLogin = redisService.hasKey(getTokenKey(userKey)); if (!isLogin) { return unauthorizedResponse(exchange, \"登录状态已过期\"); }
 private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) { log.error(\"[鉴权异常处理]请求路径:{}\", exchange.getRequest().getPath()); return webFluxResponseWriter(exchange.getResponse(), msg, ResultCode.FAILED_UNAUTHORIZED.getCode()); }
 FAILED_UNAUTHORIZED (3001, \"未授权\"),

我们可以这样处理
因为后端对于token过期会报错的,就在网关中,会报登录状态已过期,会报3001的错误,,,而且我们还有响应拦截器,所以就可以在响应拦截器中进行处理了,如果过期了就自动跳转到login

所以说登录状态由浏览器的token和token是否过期一起决定
我们可以去redis中删除数据,手动弄为过期
因为过期了,redis就会自动删除数据,所以我们删除它,就是模仿的过期

service.interceptors.response.use( (res) => { // 未设置状态码则默认成功状态 const code = res.data.code; const msg = res.data.msg; if(code === 3001){ ElMessage.error(msg); router.push(\'/oj/login\') removeToken(); return Promise.reject(new Error(msg)); }else if (code !== 1000) { ElMessage.error(msg); return Promise.reject(new Error(msg)); } else { return Promise.resolve(res.data); } }, (error) => { return Promise.reject(error); });

为什么要removeToken呢,因为已经过期了,没用了,但是不删掉的话,就可以一直去layout页面
注意如果是刷新的话,在全局前置守卫中,to就是自身url,而from则是根路径

这样就OK了
调试也没有错误

总结