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

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

方法一:

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

登录

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。

如何使用呢?


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 来配置防抖功能。

待完善.......

你可能感兴趣的:(性能优化,禁止,重复提交,loading,axios,防抖)