这道题来自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的方法,虽然没有解决题主的问题,但涉及到一种设计模式:装饰者模式,以不浸入源代码的方式实现开发目的。