验证码登陆业务校验逻辑剖析

574 阅读3分钟

背景

笔者这段时间在做登陆相关的业务,在没有产品的情况下,我们需要做一个比较流畅,并且符合直觉的登陆校验逻辑,在我做完之后,我想要来分析一下登陆中的“校验逻辑”。

至于具体的前后端接口逻辑,我就不详细说了。

前端这边主要用的是 antd。

具体逻辑

我们先来盘点一下,实现一个登陆页面,前端在校验方面,需要做哪些事情:

  1. 校验不合规范的手机号格式

  2. 获取验证码的时候,会有 60s 的间隔,这个间隔需要前端来实现,哪怕是在我们刷新页面的时候,我们也需要保持时间流动。

实现逻辑

  1. 那么对于“手机号格式”这个事情来说,他会有一个符合规范的标准,这个标准,我们当然是通过正则来实现的。

image.png

我们可以看到实现如下:

// utils 检测手机号是否合格
const regex = /^1[3-9][0-9]{9}$/;

export const isRightNumber = (val: string) => {
  return regex.test(val);
};

我们通过导出这个函数,放在我们的自定义校验规则里。

但是我实际在做这个功能的时候发现,如果我把他放在自定义校验规则里,就会一个情况:

“我每输入一个数字,就会产生一次校验。”

这种情况显然不符合直觉,这个问题的解决是通过给<FormItem/> 设置 validateTrigger={['onBlur', 'onSubmit']}

这样我们就可以把时机规定到 onBlur 的时候

但是还是会有一种场景:

“如果说,已经出现了错误,然后我修改手机号,只有在onBlur的时候才能去掉提示红框+提示文字”

所以我们需要客制化的处理这种情况:

<FormItem
    name='xxx'
    rules={[validatePhoneNumberRule]}
    validateTrigger={['onBlur', 'onSubmit']}
    validateStatus={!isTyping && !validatePhoneNumber(form.getFieldValue('xxx') as string) ? 'error' : ''}
    help={
          !isTyping && !validatePhoneNumber(form.getFieldValue('xxx') as string)
            ? form.getFieldsError(['xxx'])[0]?.errors[0]
            : ''
    }>
  • 这里的validateStatus就是我们红框的状态
  • help就是红框下具体的提示文案

至此,“校验不合规范的手机号格式”这一部分我们就已经实现了。

  1. 对于获取验证码的逻辑来说,我们需要处理 60s 的时间间隔

image.png

具体来说:

  1. 需要定义结束时间与剩余时间
  2. 需要实现一个 hooks 去更新剩余时间
  3. 需要提供一个 func,后续第二次获取验证码

对于第一个逻辑来说:

const [endTime, setEndTime] = useState<Date>(() => {
  const storedEndTime = localStorage.getItem('endTime');
  return storedEndTime ? new Date(storedEndTime) : new Date(Date.now());
});
const [secondsLeft, setSecondsLeft] = useState<number>(() => {
  const currentTime = new Date().getTime();
  const endTimeVal = new Date(endTime).getTime();
  return Math.max(0, Math.floor((endTimeVal - currentTime) / 1000));
});

对于第二部分逻辑来说:

// 更新剩余时间
useEffect(() => {
  const tick = () => {
    const currentTime = Date.now();
    const remainingTime = Math.max(0, (Number(endTime) - currentTime) / 1000);
    setSecondsLeft(Math.floor(remainingTime));

    if (remainingTime === 0) {
      clearInterval(intervalId);
    }
  };

  const intervalId = setInterval(tick, 1000);
  tick();

  return () => clearInterval(intervalId);
}, [endTime]);

对于第三部分逻辑来说:

// 重置倒计时
const resetCountdown = useCallback(() => {
  const newEndTime = new Date(Date.now() + 60000);
  setEndTime(newEndTime);
  localStorage.setItem('endTime', newEndTime.toISOString());
  setSecondsLeft(60);
}, []);

具体使用的话,展示倒计时是使用 {secondsLeft},而重置倒计时则是resetCountdown()

至此,验证码登陆业务校验逻辑的剖析就已经结束了~