在 Vue 项目中,有时我们需要为全局的按钮点击事件添加节流功能,防止用户的重复点击带来不必要的请求或操作。Vue 3 的事件机制和生命周期管理有别于 Vue 2,不能直接使用 $on$emit 的全局事件管理方式。在本文中,我们将实现一种全局节流方案,使得所有按钮元素自动应用节流效果,同时保持原有的按钮样式不变。

解决方案

我们的目标是确保在所有按钮点击事件上自动应用节流功能,并且不影响按钮的样式。具体实现步骤如下:

  1. 使用 pointer-events:通过 pointer-events 的属性,设置按钮在点击后短暂不可点击,同时保持按钮样式不变。
  2. 自动绑定事件监听:利用 Vue 的 mixin 生命周期,在每个按钮元素的 mounted 时添加 click 事件监听器,并在 beforeUnmount 时移除,确保事件监听器在组件销毁时清理干净,避免内存泄漏。

实现代码

下面是我们最终的代码实现,通过 app.mixin 方法实现全局的按钮节流功能:

app.mixin({
  mounted() {
    // 检查当前元素是否为按钮
    if (this.$el && this.$el.tagName === 'BUTTON') {
      // 定义节流点击事件处理器
      const handleClick = (e: Event) => {
        const button = e.currentTarget as HTMLButtonElement

        // 检查按钮是否已禁用点击,防止重复触发
        if (button.style.pointerEvents === 'none') return

        // 禁用点击,保持样式不变
        button.style.pointerEvents = 'none'
        button.style.opacity = '1' // 确保按钮样式不变

        // 设定延时,恢复按钮的点击状态
        setTimeout(() => {
          button.style.pointerEvents = 'auto'
        }, 1000) // 1秒后恢复为可点击状态,可根据需求调整
      }

      // 将处理函数存储到元素上,便于移除
      this.$el.__handleClick__ = handleClick
      this.$el.addEventListener('click', handleClick)
    }
  },
  beforeUnmount() {
    // 在组件销毁前移除事件监听器
    if (this.$el && this.$el.tagName === 'BUTTON' && this.$el.__handleClick__) {
      this.$el.removeEventListener('click', this.$el.__handleClick__)
      delete this.$el.__handleClick__ // 删除自定义属性,防止内存泄漏
    }
  }
})

代码解析

  • 节流函数 handleClick:当按钮被点击时,检查按钮的 pointer-events 属性。如果尚未禁用,则立即设置 pointer-events: none 禁止重复点击。随后,通过 setTimeout 设置 1 秒后恢复 pointer-events: auto,使按钮再次可点击。这样可以有效防止用户在短时间内多次点击按钮。
  • __handleClick__ 自定义属性:将 handleClick 函数存储在 __handleClick__ 属性上,方便在 beforeUnmount 钩子中使用。这样在组件销毁时可以通过 removeEventListener 来移除该事件,确保没有事件监听器残留,防止内存泄漏。

优势与灵活性

这种方案的优点在于:

  1. 自动化:无需手动绑定和管理,每个按钮在组件挂载时都会自动添加节流点击处理器。
  2. 样式保持:通过 pointer-events 控制按钮的点击,不会影响原有的按钮样式。
  3. 易于维护:事件监听器在组件销毁时自动移除,确保资源的有效管理。