Uniapp和 Vue3项目中防止按钮重复点击的3种方案
- uniapp
- 1天前
- 10热度
方案一:状态锁
核心:通过设置变量 true/false 来控制按钮或者 view 的状态。
这种方式应该最常见。
<template>
<view>
<em><!-- 作者:知否技术 --></em>
<em><!--1. 视图 --></em>
<view :class="{ 'disabled': isLoading }" @click="handleSubmit">
提交
</view>
<em><!--2. 按钮 disabled --></em>
<uv-button :disabled="isLoading" @click="handleSubmit"> 提交 </uv-button>
<em><!--3. 按钮 loading --></em>
<uv-button :loading="isLoading" @click="handleSubmit">提交</uv-button>
</view>
</template>
<script setup>
import {ref} from 'vue'
import orderApi from '@/api/order/order.js';
const isLoading = ref(false)
// 提交方法
const handleSubmit = async () => {
// 防止重复点击
if (isLoading.value) return
// 锁住
isLoading.value = true
try {
const res = await orderApi.handleSubmit(order);
if (res.code === 200) {
getReturnFundList();
uni.showToast({ title: '提交成功' });
} else {
uni.showToast({ title: '提交失败' });
}
} catch (error) {
console.error("提交失败:", error);
} finally {
// 解锁
isLoading.value = false;
}
}
</script>Code language: HTML, XML (xml)
方案二:全局自定义防重复点击指令
在项目根目录中新建 /directives/preventClick.js :

1. preventClick.js
<em>// 防止重复点击指令</em>
exportconst preventClick = {
<em>// 指令绑定到元素时执行</em>
mounted(el, binding) {
<em>// 1. 设置防抖间隔时间,默认1000毫秒</em>
const delay = binding.value || 1000;
<em>// 2. 创建一个标记,记录当前是否可点击</em>
el.isDisabledClick = false;
<em>// 3. 保存原始的点击事件处理函数</em>
const originalClickHandler = el.__originalClickHandler;
<em>// 4. 创建新的点击事件处理函数</em>
el.__preventClickHandler = (event) => {
<em>// 如果当前不可点击,直接拦截点击事件</em>
if (el.isDisabledClick) {
event.preventDefault(); <em>// 阻止默认行为</em>
event.stopPropagation(); <em>// 阻止事件冒泡</em>
return;
}
<em>// 设置为不可点击状态</em>
el.isDisabledClick = true;
<em>// 5. 执行原始的点击事件处理函数</em>
if (originalClickHandler) {
originalClickHandler(event);
}
<em>// 6. 设置定时器,在指定时间后恢复点击</em>
el.preventClickTimer = setTimeout(() => {
<em>// 恢复点击状态</em>
el.isDisabledClick = false;
<em>// 清除定时器引用</em>
el.preventClickTimer = null;
}, delay);
};
<em>// 7. 移除原始的点击事件,添加新的点击事件</em>
el.removeEventListener('click', originalClickHandler);
el.addEventListener('click', el.__preventClickHandler);
},
<em>// 指令从元素解绑时执行</em>
unmounted(el) {
<em>// 8. 清除定时器</em>
if (el.preventClickTimer) {
clearTimeout(el.preventClickTimer);
el.preventClickTimer = null;
}
<em>// 9. 移除事件监听器</em>
if (el.__preventClickHandler) {
el.removeEventListener('click', el.__preventClickHandler);
el.__preventClickHandler = null;
}
<em>// 10. 清除自定义属性</em>
el.isDisabledClick = null;
el.__originalClickHandler = null;
}
};Code language: JavaScript (javascript)
2. 在 main.js 中全局注册指令
import { createSSRApp } from'vue';
import App from'./App.vue';
<em>// 导入指令</em>
import { preventClick } from'@/directives/preventClick';
exportfunction createApp() {
const app = createSSRApp(App);
<em>// 全局注册指令,指令名为 prevent-click</em>
app.directive('prevent-click', preventClick);
return { app };
}Code language: JavaScript (javascript)
3.在组件中使用指令
<template>
<view>
<em><!-- 方式1:使用默认 1000ms 防抖 --></em>
<button v-prevent-click @click="handleClick">点击</button>
<em><!-- 方式2:自定义防抖时间 --></em>
<button v-prevent-click="2000" @click="handleClick">点击</button>
</view>
</template>Code language: HTML, XML (xml)
方案三:封装一个防重复点击的 Hook
在项目目录 hooks 新建文件: usePreventReclick.js

import { ref } from'vue'
exportfunction usePreventReclick(delay = 1000) {
const isDisabled = ref(false)
<em>// 执行受保护的操作 fn - 要执行的函数(建议返回 Promise)</em>
<em>// 返回 fn 的结果或 undefined(如果被阻止)</em>
const execute = async (fn) => {
if (isDisabled.value) {
console.info('操作被阻止')
returnundefined
}
<em>// 设置为 true</em>
isDisabled.value = true
try {
const result = await fn()
return result
} catch (error) {
<em>// 抛出异常</em>
throw error
} finally {
<em>// 延迟后自动重置状态</em>
setTimeout(() => {
isDisabled.value = false
}, delay)
}
}
<em>// 解除禁用</em>
const reset = () => {
isDisabled.value = false
}
return {
isDisabled,
execute,
reset
}
}Code language: JavaScript (javascript)
在项目中使用:
<template>
<view class="container">
<button
:disabled="isLoading"
:loading="isLoading"
@click="handleSubmit"
>
{{ isLoading ? '正在提交中...' : '提交订单' }}
</button>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { usePreventReclick } from '@/hooks/usePreventReclick'
const isLoading = ref(false)
// 创建防重实例:时间 1500 毫秒
const { isDisabled: isLoading, execute } = usePreventReclick(1500)
const handleSubmit = () => {
execute(async () => {
// 模拟后端请求
await new Promise()
uni.showToast({ title: '提交成功'})
}).catch(err => {
console.error('提交失败:', err)
uni.showToast({ title: '提交失败'})
})
}
</script>Code language: HTML, XML (xml) 