平时我们在使用软件的时候常见的扫码登录是怎么实现的呢?今天学习了一下
具体的实现流程是需要后端提供三个接口
- 获取二维码的
key
- 依据获取到的二维码
key
获取到qrurl
来生成图片 - 最后使用轮询来判断二维码的状态,是否已经扫码,二维码是否过期,是否是在待确认亦或是等待扫码
首先我们进行前面两步,初始化一个二维码
const [qrKey, setQrKey] = useState("");
const [qrUrl, setQrUrl] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState("");
const initQrCode = async () => {
const {
data: { unikey },
} = await getQrCodeKey();
const {
data: { qrurl },
} = await getQrCodeImg(unikey);
setQrKey(unikey);
setQrUrl(qrurl);
setIsLoading(false);
};
这里我是用的 react
在 useEffect
里面调用初始化一下
useEffect(() => {
initQrCode();
}, []);
那我们拿到了 qrurl
之后,是一个字符串,那我们要如何给转换成二维码的形式呢?
一种方式是后端直接生成二维码图片,我们前端直接展示即可
一种是给我们 一个url
我们通过 canvas
或者是第三方库转换成二维码图片展示
一般情况下都是由前端来生成,减少对服务器的压力
这时就需要 qrcode.react
这个库
pnpm i qrcode.react
里面有一些对应的属性,比如设置宽高,前景色,背景色,透明度,容错等级,标题等等,具体属性可以去文档查看
其中里面最重要的属性就是 value
填入我们第二步返回的 qrurl
,这样二维码图片就展示出来了
下面的就是第三步,也是最重要的,就是使用轮询向检测状态的接口发送数据
这里会有几个状态,我们使用枚举定义一下
enum QRCodeStatus {
EXPIRED = 800, // 等待超时,二维码失效
WAITING = 801, // 等待扫码
CONFIRMED = 802, // 已经扫码,等待确认登录
SUCCESS = 803, // 确认登录
}
轮询检查二维码状态
useEffect(() => {
if (!qrKey) return;
// 使用间歇定时器,每 1.5s 发送一条请求查看二维码状态
const timer = setInterval(async () => {
const { code } = await checkQrCodeStatus(qrKey);
switch (code) {
case QRCodeStatus.SUCCESS:
clearInterval(timer);
window.location.reload(); // 状态码是成功,重新刷新页面或是跳转到首页
break;
case QRCodeStatus.EXPIRED:
clearInterval(timer);
setError("二维码已过期,点击刷新"); // 设置错误提示信息
break;
}
}, 1500);
return () => clearInterval(timer); // 这里在最后要清除副作用,避免内存泄露
}, [qrKey]);
可以看到这里每 1.5s 就会发送一条请求验证二维码的状态
如果我们的状态为超时,那么就需要设置错误信息,在二维码的前置一个遮罩层提示二维码状态过期,需要重新加载
代码的具体实现
import { QRCodeSVG } from "qrcode.react";
<div className="flex flex-col items-center gap-4">
<div className="relative w-[200px] h-[200px] flex-center">
<div className=" flex-center rounded-2xl bg-white">
{isLoading ? (
<div className="text-center">正在生成二维码...</div>
) : (
<QRCodeSVG value={qrUrl} size={168} level="H" />
)}
</div>
{error && (
<div className="absolute inset-0 bg-[#fff] bg-opacity-20 backdrop-blur-sm flex flex-col items-center justify-center rounded-2xl backdrop-blur-sm">
<p className="text-white text-center px-2">{error}</p>
<Button
onClick={handleReload}
className="mt-2 !bg-blue-500 !text-white"
size="small"
>
重新获取
</Button>
</div>
)}
</div>
</div>
当然这只是扫码登录的流程的一个简单实现,具体的代码优化也可以添加 isMounted
标记,处理组件卸载后可能的 setState
的调用