为了阻止用户在某些情况下,短时间内重复点击某个按钮,导致前端向后端重复发送多次请求。
方法一:
通过控制 loading 来设置 loading,或者 disabled 也行,从而来控制按钮的是否可以点击。通过在 handleSubmit 函数未获取到服务器接口响应之前,该按钮一直处于不可用的状态,直到接收到服务器接口相应后,我们再将按钮恢复为可用状态。
<el-button type="primary" class="w312" :loading="loading" @click="handleSubmit">登录</el-button>handleSubmit () {this.loading = truesetTimeout(() => {this.loading = false}, 1000)}// 或者handleSubmit () {if (timer) {clearTimeout(timer)}timer = setTimeout(() => {this.submit()}, 300)}
进阶版:
export const operationConfirm = function (attr, message) {const that = thisreturn new Promise((resolve, reject) => {that.$antdConfirm({title: '提示',content: message || `确定要执行${attr}操作吗?`,onOk () {resolve(true)},onCancel () {resolve(false)},})})}// loading 处理重复提交(注意 Vue 实例上添加 submitLoading 字段)export const handleRepeatSubmit = async function (message, fn, cb, loading = 'submitLoading') {if (!fn) returnconst confirm = message && await operationConfirm.call(this, message)const that = thisif (!message || confirm) {if (that[loading]) returnthat[loading] = truetry {const data = await fn()cb && cb(data)that[loading] = false} catch (error) {that[loading] = false}}}// 使用如下, fn 为接口调用async handleConfirm () {const { validate, form } = this.$refs.cancelif (!await validate()) {return}handleRepeatSubmit.call(this, null, () => fn(), () => {this.visible = falsethis.$antdMessage.success('操作成功')this.$emit('refresh')})},
方法二:
使用 axios 第三方库,request 拦截器来拦截重复请求。
更多详情,请查看:axios 全局阻止重复请求。
方法三:
为避免用户在短时间内点击过快,我们给点击设置点击间隙,即做防抖处理。其实就是比较当前点击和上一次点击的时间差,如果时间差小于设置的值,即阻止点击事件,同时记录这次点击事件,以便和下次点击做比较。了解防抖基础知识,请查看:防抖和节流的区别以及实现。
// 防抖export const antiShake = (fn, t) => {let delay = t || 500let timerreturn function () {let args = arguments;if (timer) clearTimeout(timer) let callNow = !timer timer = setTimeout(() => {timer = null}, delay)if (callNow) fn.apply(this, args)}}
如何使用呢?
//引入防抖文件import { antiShake } from '@/utils/utils.js'; // 给按钮添加防抖startDrawPolygon:antiShake(function(){......})
进阶一:
除了上述实现方案,我们也可以自定义指令来实现点击事件防抖功能。
Vue.directive('antiShake', {// 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。/*** el 指令所绑定的元素,可以用来直接操作 DOM 。* binding 一个对象,包含绑定的值*/inserted: function(el, binding) {const { callback, time } = binding.valueel.callback = callbackel.time = timeel.timeCall = nullel.addEventListener('click', () => {clearTimeout(el.timeCall)el.timeCall = setTimeout(() => {el.callback()}, el.time || 500)})},......})
通过定时器 setTimeout 延时执行 click 回调,当 el.time || 500 时间内,再次触发 clearTimeout 清除定时器;
Vue.directive('antiShake', {......// 所在组件的 VNode 更新时调用update: function(el, binding) {console.log('update')const { callback, time } = binding.valueel.callback = callbackel.time = time},})
更新挂载到 el 上的 callback 和 time ,当 v-antiShake 绑定的值更新后,事件触发跟新后的 callback。
如何使用呢?
<button v-antiShake="handleClick">click</button>handleClick: {time: 1000,callback: () => {console.log(1111)}}
指令的值 handleClick 未作深度监听(watch 之 deep),只有对象整体改变才会触发指令中的 update 钩子函数。
进阶二:
当然我们全局配置防抖,即把 click 添加的防抖处理事件,添加到 Vue 实例上。
const on = Vue.prototype.$on// 防抖处理Vue.prototype.$on = function (event, func) {let timerlet newFunc = funcif (event === 'click') {newFunc = function () {clearTimeout(timer)timer = setTimeout(function () {func.apply(this, arguments)}, 500)}}on.call(this, event, newFunc)}
参考案例:
vue全局配置防抖和节流
防抖与节流(vue-自定义指令)
vue 自定义防抖/节流指令的实现
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
更新
使用 lodash 库的 debounce 来配置防抖功能。
待完善.......