vue防止按钮重复点击方案
vue防止按钮重复点击方案
按钮添加防止重复点击的原因有以下几个:
- 用户操作习惯: 有些用户习惯性地快速点击多次,尤其是在感觉应用响应慢的时候。
- 网络延迟: 点击按钮后,请求发送到服务器并返回响应需要时间。在此期间,如果按钮没有及时给出反馈或禁用,用户可能会认为第一次点击无效而再次点击。
- 程序 Bug: 有时程序逻辑错误可能导致按钮状态未正确更新。
防止重复点击的方法有以下几种:
1.禁用按钮与状态锁定
在按钮点击时,将按钮设置为禁用状态,防止用户再次点击。当请求完成后,再将按钮恢复为可用状态。
<template><buttonid="submitBtn":class="{ 'is-loading-css': isLoading }"@click="handleSubmitWithCSS":disabled="isLoading">{{ buttonText }}</button>
</template><script setup lang="ts">
const buttonText = ref("提交");
const isLoading = ref(false);
const handleSubmitWithCSS = async () => {if (isLoading.value) return;isLoading.value = true;buttonText.value = "正在提交...";try {// 模拟异步操作await new Promise((resolve) => setTimeout(resolve, 2000));} catch (err) {console.log(err);} finally {isLoading.value = false;buttonText.value = "提交";}
};
</script><style scoped>
#submitBtn {padding: 10px 20px;background-color: #007bff;color: white;
}
/* 添加加载状态样式 */
.is-loading-css {position: relative;cursor: not-allowed;opacity: 0.7;
}/* 可选的加载动画 */
/* 可结合element el-loading 使用 */
.is-loading-css::after {content: "";position: absolute;right: 10px;top: 50%;width: 16px;height: 16px;margin-top: -8px;border: 2px solid rgba(255, 255, 255, 0.3);border-radius: 50%;border-top-color: white;animation: spin 0.8s linear infinite;
}@keyframes spin {to {transform: rotate(360deg);}
}
</style>
2.CSS pointer-events: none
点击后,给按钮添加一个 CSS 类,设置 pointer-events: none;,使其不再响应鼠标事件
...代码同上//css部分
.is-loading-css {pointer-events: none;
}
3.自定义指令
- 新建 directives/throttleClick.js
//directives/throttleClick.js
import type { DirectiveBinding } from "vue";
// /获取统一配置对象
function getConfig(value) {if (typeof value === "function") {return {handler: value,loadingText: "处理中...",errorHandler: undefined,};}return {handler: value.handler,loadingText: value.loadingText,errorHandler: value.errorHandler,};
}
export default {mounted(el: Element, binding: DirectiveBinding) {console.log(el, binding);const { value, arg } = binding;const delay = arg ? parseInt(arg) : 1500; // 默认防重时间 1.5sconst config = getConfig(value); // 获取配置对象// 添加自定义禁用类名const disabledClass = "is-loading-css";el.addEventListener("click", async () => {// 检查是否已禁用if (el.classList.contains(disabledClass)) return;// 添加禁用状态el.classList.add(disabledClass);const originalText = el.textContent;if (binding.value?.loadingText) {el.textContent = binding.value.loadingText;}el.textContent = config.loadingText;try {// 执行绑定的函数const result = config.handler();// 如果返回的是 Promise,等待其完成if (result instanceof Promise) {await result;}} catch (error) {console.error("指令捕获的错误:", error);} finally {// 延迟后恢复按钮setTimeout(() => {el.classList.remove(disabledClass);el.textContent = originalText;}, delay);}});},
};
- 注册指令
//main.js
import { createApp } from "vue";
import App from "./App.vue";import throttleClick from "./directives/throttleClick"; // 导入自定义指令const app = createApp(App);app.directive("throttleClick", throttleClick);
app.mount("#app");
- 使用指令
<template><!-- 自定义指令用法1 --><!-- <buttonid="submitBtn"v-throttle-click:1000="{handler: handleSubmitWithCSS,loadingText: '提交中...',errorHandler: handleError}">{{ buttonText }}</button> --><!-- 自定义指令用法2 --><button v-throttle-click="handleSubmitWithCSS" id="submitBtn">{{ buttonText }}</button>
</template><script setup lang="ts">
const buttonText = ref("提交");const handleSubmitWithCSS = async () => {await new Promise((resolve) => setTimeout(resolve, 2000));console.log("表单提交成功!");
};
// const handleError = (err) => {
// console.log(err); // 处理错误逻辑
// }
</script><style scoped>
#submitBtn {padding: 10px 20px;background-color: #007bff;color: white;
}
/* 添加加载状态样式 */
.is-loading-css {position: relative;cursor: not-allowed;opacity: 0.7;/* 点击后,给按钮添加一个CSS类,设置 pointer-events: none;,使其不再响应鼠标事件。 *//* pointer-events: none */
}/* 可选的加载动画 */
.is-loading-css::after {content: "";position: absolute;right: 10px;top: 50%;width: 16px;height: 16px;margin-top: -8px;border: 2px solid rgba(255, 255, 255, 0.3);border-radius: 50%;border-top-color: white;animation: spin 0.8s linear infinite;
}@keyframes spin {to {transform: rotate(360deg);}
}
</style>
4.防抖函数(Debounce)
- 防抖函数是一种延迟执行的函数,只有在一定时间内没有再次触发函数时才会执行。
<template><button @click="handleSubmit" id="submitBtn">提交</button>
</template><script setup lang='ts'>import { debounce } from 'lodash'; // 引入 lodash 的防抖函数const handleSubmitWithCSS = async() => {await new Promise(resolve => setTimeout(resolve, 2000));console.log('表单提交成功!');
}
const handleSubmit =debounce(async function() {handleSubmitWithCSS();}, 2000, { leading: false, trailing: true }); // 防抖函数,延迟2秒执行</script>