Fork me on GitHub

兼职答题第一道被采纳的题

这道题来自CSDN,最近接了一个兼职在CSND做答题服务。

这道题是网友提出来了的,题干如下:

请问有没有办法用vue或者原生JS将多个页面统一进行防止重复执行点击事件(在不侵入源事件方法的前提下,因为页面太多是在改不过来),或者进行统一节流处理

我给出的第一个答案:

如果点击事件都有axios请求,你防止的是重复的后端请求,如果是vue单页面项目可以考虑如下的方案: 取消重复请求,具体方法如下,基本思路是缓存,请求结束之后清空缓存

/**
 * 全部请求拦截器处理
 */
const pendingRequests = new Map()
myAjax.interceptors.request.use(function (config) {
  const params = config.params || config.data
  const requestKey = `${config.url}/${JSON.stringify(params)}&request_type=${config.method}`
  if (pendingRequests.has(requestKey)) {
    config.cancelToken = new axios.CancelToken((cancel) => {
      // cancel 函数的参数会作为 promise 的 error 被捕获
      cancel(`重复的请求被主动拦截: ${requestKey}`)
    })
  } else {
    pendingRequests.set(requestKey, config)
    config.requestKey = requestKey
  }
  return config
},
function (error) {
  pendingRequests.clear()
  return Promise.reject(error)
}
)
/**
 * 响应拦截器
 */
myAjax.interceptors.response.use(response => {
  const requestKey = response.config.requestKey
  pendingRequests.delete(requestKey)
  return response
}, error => {
  pendingRequests.clear()
  return Promise.reject(error)
})

或者将问题阐述具体一些。

之所以这样是他的题干信息不具体,而且当是我正在优化全局响应拦截器这部分。我很自然的想到是不是他也做一层拦截就可以了。然后我给出全局拦截的前提还有解决方案。

后来,他回复了,回复内容:

不是单页面项目,后台请求基本都是普通的ajax(应该是不像axios有拦截器的..)。主要是对每个页面的nav标签里的请求按钮进行统一的防重复点击、请求。

然后我就知道他要做什么了,是想对所有类似的按钮做一层防抖。于是我给出了我的第二个答案:

可以利用css的选择器拿到按钮, 然后拿到按钮绑定事件的引用, 然后重新给按钮绑定事件, 重写事件的方法,加上防抖。

<body>
    <button id="btn" onclick="alert('hi~')">我是按钮</button>
    <script>
        const btn = document.getElementById('btn')
        const clickfn = btn.onclick // 获取onclik的方法
        btn.onclick = null //解除事件
        console.log(clickfn, 'click')
        let timer = null
        // 利用addEventListener重新绑定事件 顺便加上防抖
        btn.addEventListener('click', ()=>{
            timer && clearTimeout(timer)
            timer = setTimeout(() => {
                clickfn()
            }, 300);
        })
    </script>
</body>

将上面写成一个单独的JavaScript文件,引入到各个html文件中。 当然前提是你是利用onclick绑定的事件,如果是利用addEventListener绑定的事件:也可以参照这个思路,但前提是能够拿到addEventListener绑定的事件。目前我没找到合适的方法拿到。

给出这个方案也是基于《设计模式》里面有提到的装饰者模式,尽可能少的减少原来代码的情况下,解决问题。

但是题主随后表示和我一样不能拿到addEventListener绑定事件的引用。

最后我又想到了一个方案,于是再度回复:

(function() { 
    Element.prototype._addEventListener = Element.prototype.addEventListener; 
    Element.prototype.addEventListener = function(a, b, c) { 
     this._addEventListener(a, b, c); 
     if (a == "click") { 
      this._addEventListener("touchstart", b, c); 
     } 
    }; 
})();

试试复写addEventListener这个思路。

我相信这个思路一定是可以解决的。然后我耐心等待题主的回复。可题主就像不在线了一样,一直到我快下班了都没回复。我因为着急别的事情就不在关注。

第二天没想到的是题主回复通过借鉴第一个回复解决了问题。并且表示多谢我,我于是在他的回复下面麻烦他采纳我的答案,没想到他真的采纳了。

我心里一阵高兴。

总结:就解决题主的问题而言,防止重复请求的可以用全局拦截器,也可以用防抖。就题主的场景而言,我最后的答案是可以解决他的问题的。实质是对事件绑定过程进行拦截。而利用拿到onclick的方法,虽然没有解决题主的问题,但涉及到一种设计模式:装饰者模式,以不浸入源代码的方式实现开发目的。

posted @ 2021-10-24 10:52  我站在山顶上  阅读(35)  评论(0编辑  收藏  举报