记一次需求的解决-限制按钮频繁触发

背景

最近,一个朋友叫我帮他解决一个问题,事情是这样的:
他们开发的产品(这个项目的前端是使用vue实现的)即将上线,但是在测试时发现后台竟然挂了,通过查找日志,他们发现是由于接口被频繁请求,导致后台崩溃了。他们猜测是由于按钮点击没有做限制,用户频繁点击导致请求多次触发,一时又定位不出问题的所在,因此希望给所有按钮加上一个限制,让其无法重复提交。
本来这个问题,理应从后台着手处理,但后台处理的成本比较高,一来可能赶不及,二来本身这些问题最好前端也进行限制,进行节流,他希望我帮他处理这个问题。

思考

由于他们这个项目已经完成,我当时简单统计了一下,他们的点击事件有100多个,要逐个添加工作量也很大,而且容易出错。再者,我对他们的项目不了解,简单粗暴的限制按钮无法重复提交可能存在风险,也许这个点击就是可以多次触发的,直接修改成只能触发请求可能存在风险。

因此,经过和朋友的确认,将需求改为用户的点击不能频繁触发。这时我的朋友又提了一个新的要求,希望点击触发的频率可以根据情况,对每个按钮进行自定义,也就是说,每个按钮的点击的时间间隔可以不同。

我一开始写下了,限制按钮频繁触发的大致代码:

if (isClick === true) {
  return;
}
setTimeout(() => {
  isClick = false
}, time);
isClick = true

大致上限制用户频繁触发就是类似这个逻辑,但是现在的问题是这个项目已经写好了,给相应的点击事件加入类似的逻辑成本很大,而且可能会导致一些问题:譬如一个点击事件绑定的函数不仅仅是点击可以触发,还有其他情况可以触发呢?限制点击的频率问题不大,毕竟点击是用户的行为,频繁不频繁触发方法肯定不应该影响业务逻辑的,但是如果这个函数也给其他方法调用呢?可能其他方法就是希望可以连续调用,我直接给点击的方法添加这段逻辑也许就破坏了其正常的业务逻辑了。
因此我的设想是类似这样:

// 原来的点击事件
@click="fn"

// 修改成如下形式,添加限制按钮频繁触发的逻辑
@click="antiClick(fn)"

基于这个思路,我考虑如何实现代码的复用,以及保证代码的稳健,还有怎么自定义按钮的触发频率。
我的思路是这样的:
原来的点击事件A要变成antiClick的回调,点击事件会触发antiClick,如果不是时间内已经触发过,就调用原来的点击事件A,否则就不触发原来的点击事件A。间隔的时间默认是400ms,但是可以根据需求进行定义,因此用标签的属性记录这个标志位,这样就可以实现不同的标签定义不同的事件了。然后,我全局挂载antiClick,这样整个项目就都可以直接调用这个方法了。本来我也想考虑mixins或者其他的方案,后来一想,这个项目的点击事件实在是太多了,引入mixins这些比起全局引入的写法要繁琐一些,在我一个人改动这么多页面的情况下简直就是灾难,很容易出错,而且耗时也会多很多,因此便否决了。
这里还有一个点就是,点击事件是可能传入实参的,而且参数不定个数,因此我可以考虑arguments或者...等处理不定参数的方法,同时如果点击事件没写参数,vue会默认传入 e v e n t , 为 了 稳 妥 起 见 , 原 来 写 成 ‘ @ c l i c k = " f n " ‘ 的 我 都 要 传 一 个 event,为了稳妥起见,原来写成`@click="fn"`的我都要传一个 event@click="fn"event进去。

// antiClick.js
/**
 * 
 * @param {*} fn - 点击事件真正要调用的函数
 * @param {*} time - 触发后,x毫秒内不会二次触发
 * @param  {...any} args - fn要接收的参数
 * @exam 如果fn没有传入参数,vue默认会传入$event -》@click="$antiClick(fn,$event.currentTarget,$event)"
 * @exam 如果fn传入了参数,按照fn的参数传入fn(index) -> @click="$antiClick(fn,$event.currentTarget,index)"
 */
export default function antiClick(fn, el, ...args) {
  if (el.getAttribute('data-have-click') === 'true') {
    return;
  }
  const time = el.getAttribute('data-time') * 1 || 400;
  setTimeout(() => {
    el.setAttribute('data-have-click', 'false');
  }, time);
  el.setAttribute('data-have-click', 'true');
  fn.call(this, ...args);
}
import antiClick from 'antiClick的路径';
Vue.prototype.$antiClick = antiClick;

使用类似如下:

<button @click="$antiClick(test,$event.currentTarget,12)" data-time="2000"> 测试按钮</button>

一般写法:

如果方法没有主动传入参数
例如:@click="myFn",vue会默认传入$event,因此写法如下:
@click="$antiClick(myFn,$event.currentTarget,$event)"
如果方法有主动传入参数
例如:@click="myFn(index)",写法如下:
@click="$antiClick(myFn,$event.currentTarget,index)"

// 默认限制是400毫秒,即触发一次事件后400毫秒内不会再次触发;data-time可以时间,单位毫秒
<div @click="$antiClick(myFn,$event.currentTarget,$event)" data-time="1000"></div>

方法写好了,但接下来的工作就是一个方法一个方法的检查,确认它是否会触发请求了,因为我也不想改动过大,希望尽可能只对与请求有关的点击事件进行处理。基本上就是体力活了,一个一个的检查过去,当然修改时也有使用正则配合替换来提高效率,降低出错的可能,但这个也是在检查后,在一些比较稳的代码去处理的,毕竟感觉直接用正则处理风险还是比较高的。

说点题外话,还好我没有直接用正则进行处理,在检查过程中我发现,有一些点击事件是类似这样的:@click="a=1",我当时是没有考虑到这种情况的,如果直接替换,可能会出错啊。

最终替换还是花费了我不少的功夫,但这已经是我所想到的,修改成本最低的方案了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值