效果
点击登录后,显示二维码→打开“探古”(本项目)微信小程序,扫描二维码确认登录→web端登录成功
主要流程
因为本人主要负责web前端的开发,所以本文仅介绍web前端的实现方法
代码
生成随机值
我这里使用的是crypto.getRandomValues()
方法,该方法可以获取符合密码学要求的安全的随机值,具体可参考:mdn web docs Crypto.getRandomValues()
二维码的生成使用的是qrcode.react包的QRCode组件,其value属性存储生成的随机值
// 获得随机的qr值
function getQRvalue() {
const array = new Uint32Array(1);
// Crypto.getRandomValues() 方法让你可以获取符合密码学要求的安全的随机值
window.crypto.getRandomValues(array);
let random = array[0].toString();
setQrvalue(random)
console.log(random)
return random;
}
实现带倒计时的轮询
这一部分需要非常注意setInterval的闭包属性!
1. 实现二维码展示的倒计时
// qrCountdown为展现在页面上的剩余时间
let t = qrCountdown;
let qrTimer = setInterval(() => {
console.log("in qrtimer:", t)
t--;
setQrCountdown(t)
// 到时间
if (t < 0) {
// qrTimer是二维码倒计时计时器
clearInterval(qrTimer)
// timerInter是定义在全局的轮询计时器
clearInterval(timerInter)
setLoginModalVisible(false)
// 重置展现在页面上的剩余时间
setQrCountdown(60)
}
}, 1000)
// 设置qrTimer为一个状态
setQrTimer(qrTimer);
注意这里setInterval第一个参数方法内部不能直接使用qrCountdown去作为上一个值来减!因为它会产生一个闭包,记住qrCountdown的值,所以实际上每次调用,qrCountdown都是原来的值,不会因为setQrCountdown而发生变化,会造成错误
2. 实现轮询计时器
2.1 参数列表
function getLoginInfo(rand, stop, qrTimer) {
}
传入参数:
- 二维码带的rand值
- 是否不重复倒计时
主要是判断是否是第一次调用该方法,如果是第一次调用,则设置计时器;否则跳过。 - 二维码倒计时器
为什么需要把这个传入呢?因为在进行setInterval时,内部会产生一个闭包,在内部设置清除计时器清除的是一开始进入getLoginInfo方法时的计时器,即初始计时器编号(我设置的是0),而不是后面经过setInterval赋值以后的计时器编号。因为它内部产生了一个闭包,即使外部的计时器编号已经发生了改变,但是它内部依然不会变!
所以我这里在二维码计时器已经定义好了以后,即上一部分实现二维码展示的倒计时的代码后面,调用该方法,将已定义好的二维码倒计时计时器qrTime传入该方法中
2.2 具体实现
let timerInter;
function getLoginInfo(rand, stop, qrTimer) {
console.log("polling")
console.log("getLoginInfo timerInter:", timerInter)
axios({
method: 'post',
url: '/api/user/getLoginInfo',
// 传入二维码的rand
data: qs.stringify({
rand
})
})
.then((res) => {
// 如果轮询成功
if (res.data.success) {
console.log(res.data.data)
// 设置用户参数
setUserInfo(res.data.data)
// 清除当前的轮询计时器
clearInterval(timerInter)
// 清除二维码倒计时计时器
clearInterval(qrTimer)
setHasLogined(true)
setLoginModalVisible(false)
setQrCountdown(60)
Modal.success({
content: "登录成功!"
})
} else {
// 轮询未获得
console.log(res.data.message)
// stop表示是否是第一次轮询
// !stop为true表示是第一次调用该函数,则设置计时器
if (!stop) {
// 每隔4秒调用一次该方法
// 并将第二个参数改为true,表示后面不需要再设置计时器
// 并将二维码倒计时计时器传入
timerInter = setInterval(() => getLoginInfo(rand, true, qrTimer), 4000)
// 记录轮询计时器的编号,方便在外部对该计时器进行清除
setTimer(timerInter)
}
}
})
.catch((error) => console.log(error))
}
这里设置了一个轮询计时器编号的状态time,并通过setTimer()把当前计时器的编号记录下来了。因此,如果用户点击主动关闭二维码框,也就是调用Modal的onCancel()方法(这个方法在闭包外面),也可以去清除轮询计时器,中止轮询。