Vue 项目中,点击多次按钮禁止重复提交数据

为了阻止用户在某些情况下,短时间内重复点击某个按钮,导致前端向后端重复发送多次请求。

方法一:

通过控制 loading 来设置 loading,或者 disabled 也行,从而来控制按钮的是否可以点击。通过在 handleSubmit 函数未获取到服务器接口响应之前,该按钮一直处于不可用的状态,直到接收到服务器接口相应后,我们再将按钮恢复为可用状态。

<el-button type="primary" class="w312" :loading="loading" @click="handleSubmit">登录</el-button>

handleSubmit () {
  this.loading = true
  setTimeout(() => {
    this.loading = false
  }, 1000)
}

// 或者
handleSubmit () {
  if (timer) {
    clearTimeout(timer)
  }
  timer = setTimeout(() => {
    this.submit()
  }, 300)
}
进阶版:
export const operationConfirm = function (attr, message) {
  const that = this
  return 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) return
  const confirm = message && await operationConfirm.call(this, message)
  const that = this
  if (!message || confirm) {
    if (that[loading]) return
    that[loading] = true
    try {
      const data = await fn()
      cb && cb(data)
      that[loading] = false
    } catch (error) {
      that[loading] = false
    }
  }
}


// 使用如下, fn 为接口调用
async handleConfirm () {
  const { validate, form } = this.$refs.cancel
  if (!await validate()) {
    return
  }

  handleRepeatSubmit.call(this, null, () => fn(), () => {
    this.visible = false
    this.$antdMessage.success('操作成功')
    this.$emit('refresh')
  })
},

方法二:

使用 axios 第三方库,request 拦截器来拦截重复请求。

更多详情,请查看:axios 全局阻止重复请求

方法三:

为避免用户在短时间内点击过快,我们给点击设置点击间隙,即做防抖处理。其实就是比较当前点击和上一次点击的时间差,如果时间差小于设置的值,即阻止点击事件,同时记录这次点击事件,以便和下次点击做比较。了解防抖基础知识,请查看:防抖和节流的区别以及实现

// 防抖
export const antiShake = (fn, t) => {
  let delay = t || 500
  let timer
  return 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.value
    el.callback = callback
    el.time = time
    el.timeCall = null
    el.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.value
    el.callback = callback
    el.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 timer
  let newFunc = func
  if (event === 'click') {
    newFunc = function () {
      clearTimeout(timer)
      timer = setTimeout(function () {
        func.apply(this, arguments)
      }, 500)
    }
  }
  on.call(this, event, newFunc)
}

参考案例:

vue全局配置防抖和节流

防抖与节流(vue-自定义指令)

vue 自定义防抖/节流指令的实现

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

更新

使用 lodash 库的 debounce 来配置防抖功能。

待完善.......

  • 6
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue.nextTick()的实现原理是将回调函数推入到一个队列,在下一个事件循环周期(MacroTask)执行这个队列的所有回调函数。具体来说,当用户使用Vue.nextTick()执行回调函数时,Vue.js会按照以下步骤进行处理:\[2\] 1. 将回调函数推入到一个队列。 2. 在下一个事件循环周期,执行队列的所有回调函数。 3. 这样可以确保在DOM更新后执行回调函数,以便获取最新的DOM状态。 Vue使用异步队列的方式来控制DOM更新和nextTick回调的先后执行。由于MicroTask具有高优先级特性,Vue将回调函数放入MicroTask队列,以确保在一次事件循环前被执行完毕。为了兼容性问题,Vue还实现了MicroTask向MacroTask的降级方案,以确保回调函数的执行顺序正确。\[3\] #### 引用[.reference_title] - *1* *2* [vue nextTick原理详解](https://blog.csdn.net/qq_34185872/article/details/130221892)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [vue的nextTick原理](https://blog.csdn.net/chenzeze0707/article/details/90083725)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值