同学,请实现一个扫码登录

大概的流程图如下

5a41cfce7dc04c8f91ffa988c39e08ac.png

主要涉及到的是pc端、手机端和后台服务端。由于听产品同事说手机端由原生端(安卓和IOS)来实现,因此我这边只需要开发pc端就行,工作量直接减半有没有。做过该功能的小伙伴肯定了解,pc端的实现还是比较简单的,主要就是开启轮询查询后台扫码状态,然后做对应的提示或登录成功后跳转首页。

扫码登录的需求在前端主要难点在轮询上

0. 什么叫轮询?

所谓的轮询就是,由后端维护某个状态,或是一种连续多篇的数据(如分页、分段),由前端决定按序访问的方式将所有片段依次查询,直到后端给出终止状态的响应(结束状态、分页的最后一页等)。

1. 轮询的方案?

一般有两种解决方案:一种是使用websocket,可以让后端主动推送数据到前端;还有一种是前端主动轮询(上网查了下细分为长轮询和短轮询),通过大家熟悉的定时器(setIntervalsetTimeout)实现。

由于项目暂未用到websocket,且长轮询需要后台配合,所以直接采用短轮询(定时器)开撸了。

遇到的问题:

1、由于看需求文档上交互流程比较清晰,最开始没去网上查找实现方案,自己直接整了一版setInterval的轮询实现。在跟后台联调的过程中发现定时器每1s请求一次接口,发现很多接口没等响应就开启下一次的请求,很多请求都还在pending中,这样是不对的,对性能是很大消耗。于是想了下,可以通过setTimeout来优化,具体就是用setTimeout递归调用方式模拟setInterval的效果,达到只有上一次请求成功后才开启下一次的请求。

// 开启轮询async beginPolling() {if (this.isStop) return;try {const status = await this.getQrCodeStatus();if (!status) return;this.codeStatus = status;switch(this.codeStatus) {case '2':this.stopPolling();// 确认登录后,需前端修改状态this.codeStatus = '5';this.loading = true;// 走登录逻辑this.$emit('login', {qrcId: this.qrcId,encryptCSIIStr: this.macAddr})break;case '3':// 取消登录this.stopPolling();await this.getQrCode();break;case '4':// 二维码失效this.stopPolling();break;default:break;}this.timer = setTimeout(this.beginPolling);} catch(err) {console.log(err);this.stopPolling();}},

2、在自测了过程中又发现了另外一个问题,stopPolling方法中clearTimeout似乎无法阻止setTimeout的执行,二维码失效后请求仍在不停发出,这就很奇怪了。上网搜索了一番,发现一篇文章(很遗憾,已经找不到是哪篇文章了!)记录了这个问题:大概意思是虽然clearTimeout已经清除了定时器,但此时有请求已经在进行中,导致再次进入了循环体,重新开启了定时器。解决办法就是,需要手动声明一个标识位isStop来阻止循环体的执行。

    stopPolling() {if (this.timer) {clearTimeout(this.timer);this.timer = null;// 标记终止轮询(仅clearTimeout无法阻止)this.isStop = true;}},

试了下确实达到效果了,但其实这个问题产生的具体原因我还是有些模糊的,希望遇到过相关问题的大佬能指点一下,感激不尽!

3、解决了上面提到的问题,就在以为万事大吉,只待提测的时候。后台同事发现了一个问题(点赞后台同事的尽责之心):他在反复切换登录方式(扫码登录<->账号密码登录)的过程中,发现后台日志有一段时间打印的qrcId不是最新的。然后我这边试了下,确实在切换频率过高时,此时有未完成的请求仍在进行中,导致qrcId被重新赋值了。虽然已经在beforeDestroy里调用了stopPolling清除定时器,但此时请求是未停止的。聪明的小伙伴们肯定想到axioscancelToken可以取消未完成的请求,但我实际也并没有用过,而且项目里也没有可以表演Ctrl+CCtrl+V的地方。于是百度了一番,找到一篇掘友的文章,为了表示尊敬我原封不动的搬到我的代码里了,哈哈!

import axios from "axios";
const CancelToken = axios.CancelToken;const cancelTokenMixin = {data() {return {cancelToken: null, // cancelToken实例cancel: null, // cancel方法};},created() {this.newCancelToken();},beforeDestroy() {//离开页面前清空所有请求this.cancel("取消请求");},methods: {//创建新CancelTokennewCancelToken() {this.cancelToken = new CancelToken((c) => {this.cancel = c;});},},
};
export default cancelTokenMixin;

在组件里引入mixin,另外在请求时传入cancelToken实例,确实达到效果了。此时再次切换登录方式,之前的未完成的请求已被取消,也就无法再篡改qrcId。写到此处,我发现问题2也是未完成的请求导致的,那么是否可以不用isStop标识,直接在stopPolling中调用this.cancel("取消请求");不是更好吗?

完整代码如下:

import sunev from 'sunev'; // 全局公共方法库
import cancelTokenMixin from "@/utils/cancelTokenMixin"; // axios取消请求export default {props: {loginType: {type: String,default: 'code'}},mixins: [cancelTokenMixin],data() {return {qrcId: '', // 二维码标识qrcBase64: '', // 二维码base64图片macAddr: '', // mac地址loading: false,isStop: false,codeStatus: '0',qrStatusList: [{status: '-1',icon: 'error',color: '#ed7b2f',svgClass: 'icon-error-small',text: '二维码生成失败\n请刷新重试',refresh: true},{ status: '0', icon: '', text: '', refresh: false },{status: '1',icon: 'scan',color: '#2986ff',svgClass: 'icon-scan-small',text: '扫描成功\n请在移动端确认',refresh: false},{status: '2',icon: 'confirm',color: '#2986ff',svgClass: 'icon-confirm-small',text: '移动端确认登录',refresh: false},{status: '3',icon: 'cancel',text: '移动端已取消',refresh: false},{status: '4',icon: 'error',color: '#ed7b2f',svgClass: 'icon-error-small',text: '二维码已失效\n请刷新重试',refresh: true},{status: '5',icon: 'success',color: '#2986ff',svgClass: 'icon-success-small',text: '登录成功',refresh: false},{status: '6',icon: 'error',color: '#ed7b2f',svgClass: 'icon-error-small',text: '登录失败\n请刷新重试',refresh: true}],errMsg: ''}},async created() {try {await this.getQrCode();this.beginPolling();} catch(err) {console.log(err);}},computed: {// 当前状态curQrStatus() {const statusObj = this.qrStatusList.find(item => item.status === this.codeStatus);if (this.errMsg) {statusObj.text = this.errMsg;}return statusObj;}},methods: {// 开启轮询async beginPolling() {if (this.isStop) return;try {const status = await this.getQrCodeStatus();if (!status) return;this.codeStatus = status;switch(this.codeStatus) {case '2':this.stopPolling();// 确认登录后,需前端修改状态this.codeStatus = '5';this.loading = true;// 走登录逻辑this.$emit('login', {qrcId: this.qrcId,encryptCSIIStr: this.macAddr})break;case '3':// 取消登录this.stopPolling();await this.getQrCode();break;case '4':// 二维码失效this.stopPolling();break;default:break;}this.timer = setTimeout(this.beginPolling);} catch(err) {console.log(err);this.stopPolling();}},// 暂停轮询stopPolling() {if (this.timer) {clearTimeout(this.timer);this.timer = null;// 标记终止轮询(仅clearTimeout无法阻止)this.isStop = true;}},// 获取二维码base64async getQrCode() {this.reset();this.loading = true;try {const params = {encryptCSIIStr: this.macAddr}const res = await sunev.$https.post('sunev/LoginQRCGen',{ isLoading: false, cancelToken: this.cancelToken },params)if (res.qrcId) {this.qrcId = res.qrcId;this.qrcBase64 = res.qrcBase64;} else {this.stopPolling();}} catch(err) {this.errMsg = err.message;this.stopPolling();}},// 获取二维码状态async getQrCodeStatus() {try {const params = {encryptCSIIStr: this.macAddr}const res = await sunev.$https.post('sunev/LoginQRCQry',{ isLoading: false, cancelToken: this.cancelToken },params)return res.status;} catch(err) {this.stopPolling();}},// 刷新二维码async refresh() {await this.getQrCode();this.beginPolling();},// 切换登录类型toggle() {this.$emit('toggleLoginType');},// 重置reset() {this.isStop = false;this.codeStatus = '0';this.errMsg = '';},beforeDestroy() {this.stopPolling();}}
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/274373.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python淘宝网页爬虫数据保存到 csv和mysql(selenium)

数据库连接设置&#xff08;表和字段要提前在数据库中建好&#xff09; # 数据库中要插入的表 MYSQL_TABLE goods# MySQL 数据库连接配置,根据自己的本地数据库修改 db_config {host: localhost,port: 3306,user: root,password: ma*****6,database: may2024,charset: utf8mb…

一体机电脑辐射超标整改

电脑一体机是目前台式机和笔记本电脑之间的一个新型的市场产物&#xff0c;它将主机部分、显示器部分整合到一起的新形态电脑&#xff0c;该产品的创新在于内部元件的高度集成。随着无线技术的发展&#xff0c;电脑一体机的键盘、鼠标与显示器可实现无线链接&#xff0c;机器只…

云打印下载,云打印怎么使用?

互联网的发展让许多实体业务都受到了强烈的冲击&#xff0c;这其中打印业务也是其中之一。在当前云打印技术的推广下&#xff0c;现在有越来越多有打印需求的用户都开始选择性价比更高、打印更方便的云打印服务了。那么云打印下载&#xff0c;云打印怎么使用&#xff1f;今天小…

AIGC——ComfyUI 安装与基础使用

简介 ComfyUI是一个基于节点流程的稳定扩散操作界面&#xff0c;通过流程实现了更加精准的工作流定制和完善的可复现性。每个模块都有特定的功能&#xff0c;我们可以通过调整模块连接来实现不同的出图效果。然而&#xff0c;节点式的工作流也提高了一定的使用门槛。同时&…

leetcode代码记录(有序数组两数之和

目录 1. 题目&#xff1a;2. 我的代码&#xff1a;小结&#xff1a; 1. 题目&#xff1a; 给定一个已按照 升序排列 的整数数组 numbers &#xff0c;请你从数组中找出两个数满足相加之和等于目标数 target 。 函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numb…

25.5 MySQL 聚合函数

1. 聚合函数 聚合函数(Aggregate Function): 是在数据库中进行数据处理和计算的常用函数. 它们可以对一组数据进行求和, 计数, 平均值, 最大值, 最小值等操作, 从而得到汇总结果.常见的聚合函数有以下几种: SUM: 用于计算某一列的数值总和, 可以用于整数, 小数或者日期类型的列…

SpringBoot(容器功能)

文章目录 1.Configuration 添加/注入bean1.注入bean1.编写一个JavaBean&#xff0c;Monster.java2.创建一个config文件夹&#xff08;名字任意&#xff09;&#xff0c;用于存放配置Bean的类&#xff08;相当于配置文件&#xff09;3.BeanConfig.java4.测试使用 MainApp.java2.…

通过Office Web Viewer站点在线展示Office文档内容

方法&#xff1a; https://view.officeapps.live.com/op/view.aspx?src经Url编码的文档线上Url地址 比如&#xff1a; //以下地址来自一份旧项目代码&#xff0c;可见用的就是该方案function OfficeFileViewOnline(url, file_type, file_name) {url "http://14.23.112.2…

RPC通信原理

RPC通信原理 RPC的概念 如果现在我有一个电商项目&#xff0c;用户要查询订单&#xff0c;自然而然是通过Service接口来调用订单的实现类。 我们把用户模块和订单模块都放在一起&#xff0c;打包成一个war包&#xff0c;然后再tomcat上运行&#xff0c;tomcat占有一个进程&am…

用chatgpt写论文重复率高吗?如何降低重复率?

ChatGPT写的论文重复率很低 ChatGPT写作是基于已有的语料库和文献进行训练的&#xff0c;因此在写作过程中会不可避免地引用或借鉴已有的研究成果和观点。同时&#xff0c;由于ChatGPT的表述方式和写作风格与人类存在一定的差异&#xff0c;也可能会导致论文与其他文章相似度高…

扩展学习|系统理解数字经济

文献来源&#xff1a;[1]肖静华,胡杨颂,吴瑶.成长品&#xff1a;数据驱动的企业与用户互动创新案例研究[J].管理世界,2020,36(03):183-205.DOI:10.19744/j.cnki.11-1235/f.2020.0041. [2]陈晓红,李杨扬,宋丽洁等.数字经济理论体系与研究展望[J].管理世界,2022,38(02):208-22413…

力扣面试经典150 —— 11-15题

力扣面试经典150题在 VScode 中安装 LeetCode 插件即可使用 VScode 刷题&#xff0c;安装 Debug LeetCode 插件可以免费 debug本文使用 python 语言解题&#xff0c;文中 “数组” 通常指 python 列表&#xff1b;文中 “指针” 通常指 python 列表索引 文章目录 11. [中等] H指…

YOLOv9改进 添加三分支注意力机制TripletAttention

一、TripletAttention论文 论文地址: 2010.03045.pdf (arxiv.org) 二、TripletAttention结构 对于输入张量,TripletAttention通过旋转操作和残差变换建立维度间依赖关系,并以可忽略的计算开销对通道间和空间信息进行编码。TripletAttention通过几乎无参数的特点来建模通道…

qt如何配置ros环境

在Qt5.7的版本可以使用bash -i -c来启动qt&#xff0c;让Qt自己识别系统环境&#xff0c;不知道为什么Qt在之后的版本&#xff0c;这样使用都失效了。因为它会默认把CMAKE_PREFIX_PATH修改掉。 网上还有安装ros插件版本的qt creator&#xff0c;感觉失去了一些灵活性。 自己测试…

数据结构 第1章:绪论

文章目录 1. 绪论1.1. 数据结构 1.2. 算法1.2.1. 算法的基本概念1.2.2. 算法的时间复杂度1.2.3. 算法的空间复杂度 1. 绪论 程序 数据结构 算法 1.1. 数据结构 数据&#xff1a;是对客观事物的符号表示&#xff0c;在计算机科学中是指所有能输入到计算机中并被计算机程序处理…

Python: 如何绘制核密度散点图和箱线图?

01 数据样式 这是数据样式&#xff1a; 要求&#xff08;我就懒得再复述一遍了&#xff0c;直接贴图&#xff09;&#xff1a; Note&#xff1a;数据中存在无效值NA&#xff08;包括后续的DEM&#xff09;&#xff0c;需要注意 02 提取DEM 这里我就使用gdal去提取一下DEM列…

深度学习图像算法工程师--面试准备(2)

深度学习面试准备 深度学习图像算法工程师–面试准备&#xff08;1&#xff09; 深度学习图像算法工程师–面试准备&#xff08;2&#xff09; 文章目录 深度学习面试准备前言一、Batch Normalization(批归一化)1.1 具体步骤1.2 BN一般用在网络的哪个部分 二、Layer Normaliza…

CTP-API开发系列之八:报撤单代码实现

CTP-API开发系列之八&#xff1a;报撤单代码实现 CTP-API开发系列之八&#xff1a;报撤单代码实现前情回顾函数实现缓存FrontID 和 SessionID报单函数实现撤单函数实现 调用示例报单&#xff08;形成挂单&#xff09;对挂单进行撤单报单&#xff08;立即成交&#xff09;注意事…

XSS靶场-DOM型初级关卡

一、环境 XSS靶场 二、闯关 1、第一关 先看源码 使用DOM型&#xff0c;获取h2标签&#xff0c;使用innerHTML将内容插入到h2中 我们直接插入<script>标签试一下 明显插入到h2标签中了&#xff0c;为什么不显示呢&#xff1f;看一下官方文档 尽管插入进去了&#xff0…

系统运维网络知识汇总

一、系统运维中网络方面的规划与思考 系统运维建立在网络的基础之上&#xff0c;如果没有一个相对合理的网络架构&#xff0c;恐怕系统运维做起来也不是那么的顺手。一个公司基本上都会把网络和服务器独立开来&#xff0c;划分不同的区域摆放设备&#xff0c;很多时候都是物理…