前端防抖:Vue自定义指令实现按钮防重复点击
📗 开发笔记· 2026-10-03 22:58 · 7

前端防抖:Vue自定义指令实现按钮防重复点击

在业务开发中,提交按钮、保存接口请求,如果用户快速多次点击,会触发多次接口调用,造成重复提交、产生脏数据等问题。除了在业务函数内部写防抖逻辑,我们可以封装 Vue 自定义指令,全局复用,统一处理按钮重复点击问题。

目录

一、业务场景痛点

  • 用户网络慢,点击提交按钮无反馈,连续多次点击按钮
  • 短时间内触发多次接口请求,新增多条重复数据
  • 每个提交按钮都单独写定时器、状态锁,代码重复,维护麻烦

方案:封装 Vue 自定义指令 v‑preventReClick,无需在每个业务方法内编写重复逻辑,直接作用于 DOM 元素,全局直接使用。

二、基础版本实现自定义防抖指令

说明:该指令原理:点击之后将按钮置为禁用状态,延时结束后解除禁用,防止短时间重复点击。延时时间支持自定义传入,不传默认3000毫秒。

js 复制代码
// main.js 或者单独 directive 文件注册
Vue.directive('preventReClick', {
  inserted(el, binding) {
    el.addEventListener('click', () => {
      // 判断按钮当前是否未禁用
      if (!el.disabled) {
        el.disabled = true
        // 取传入的延时时间,没有传则默认3000ms
        const delay = binding.value || 3000
        setTimeout(() => {
          el.disabled = false
        }, delay)
      }
    })
  }
})

代码说明

  1. inserted:指令钩子,元素插入DOM之后执行,绑定点击监听事件
  2. el:绑定指令的真实DOM元素
  3. binding.value:使用指令时传递给指令的参数,用来自定义防抖时间
  4. 点击后设置 disabled=true,按钮置灰不可点击;定时器到期恢复可用

三、组件中使用指令

基础用法:使用默认3000毫秒防抖

复制代码
<el-button type="primary" style="width:100%;" @click="handleSubmit" v-preventReClick>确 认</el-button>

自定义防抖时间,例如 2000ms

复制代码
<el-button type="primary" @click="saveData" v-preventReClick="2000">保存</el-button>
复制代码
// 业务方法,这里只写业务逻辑,不用维护loading、disabled锁
methods:{
  handleSubmit(){
    console.log("执行提交逻辑")
  },
  saveData(){
    console.log("执行保存逻辑")
  }
}

四、进阶优化版本(适配接口异步场景,推荐生产使用)

⚠️ 原实现存在缺陷:单纯定时器计时,不管接口是否成功,时间到就恢复按钮。
真实业务中,我们希望接口请求完成之后再解除禁用,而不是单纯固定时间。

优化思路:指令只做拦截,把释放权限交给业务回调。

复制代码
Vue.directive('preventReClick', {
  inserted(el, binding) {
    el.addEventListener('click', async () => {
      if (el.disabled) return
      el.disabled = true
      // 如果传入的是函数,则由业务内部手动释放 el.disabled = false
      if(typeof binding.value === 'function'){
        try{
          await binding.value()
        }finally {
          el.disabled = false
        }
      }else{
        // 普通数字:定时恢复模式
        const delay = binding.value || 3000
        setTimeout(()=>{
          el.disabled = false
        },delay)
      }
    })
  }
})

异步接口业务使用方式

复制代码
<el-button v-preventReClick="submitApi">异步提交</el-button>
复制代码
methods:{
  async submitApi(){
    // 请求接口,请求结束自动解锁按钮
    await this.$axios.post("/api/submit", this.form)
  }
}

五、注意事项与踩坑总结

  1. type 拼写问题:type="prismary" 是笔误,ElementUI正确是 type="primary"
  2. 指令操作的是原生DOM,部分自定义组件要确认 el.disabled 是否可以生效(el‑button底层会响应原生disabled属性)
  3. 两种模式选择:
    • 简单场景:定时倒计时模式,简单快速;
    • 接口异步场景:优先使用函数回调模式,接口响应完毕再释放按钮,更严谨。
  4. 该指令只解决前端层面防重复点击,重要接口后端必须也要做幂等校验,不能完全依赖前端控制。

六、扩展:抽离为独立 directive 文件(大型项目推荐)

directive/preventReClick.js

复制代码
export default {
  inserted(el,binding){
    el.addEventListener('click', async () => {
      if (el.disabled) return
      el.disabled = true
      if(typeof binding.value === 'function'){
        try{
          await binding.value()
        }finally {
          el.disabled = false
        }
      }else{
        const delay = binding.value || 3000
        setTimeout(()=> el.disabled = false, delay)
      }
    })
  }
}

main.js全局注册

复制代码
import preventReClick from './directive/preventReClick'
Vue.directive('preventReClick', preventReClick)

总结:使用自定义指令实现按钮防抖,可以把防重复点击逻辑抽离,业务代码更加干净,一处定义,全局多处复用。前端防护仅作为体验优化,幂等逻辑后端必不可少。

复制代码
---

## 博客首页卡片标题+摘要(给你博客封面用)
**标题**:Vue自定义指令实现按钮防抖|解决按钮重复点击提交
**摘要**:业务中按钮重复点击会产生重复请求,本文通过Vue自定义指令v‑preventReClick实现按钮防抖,包含基础定时版和适配异步接口的进阶版本,可直接项目复用。

### 如果你复制还是乱码,有两种备选方案
1. 我直接把**不带任何```代码块标记**的纯原文给你,你粘贴到你的博客编辑器,再在编辑器里手动点代码块按钮;
2. 我拆成一段一段文本输出给你。

需要哪一种?