完整教程:Vue3 + TypeSrcipt 防抖、防止重复点击实例

需要实现防抖应用场景:

点击【查询】按钮,发送网络请求,等待并接收响应数据

原来点击【查询】的代码:

"ts" name="ReagentTransactionsDrawer">
......
// 查询,没有防抖,可以重复点击
const onQueryClick = async () => {
// 检查
if (
queryObj.value.transactionTime.length === 0 &&
!queryObj.value.transactionType &&
!queryObj.value.materialName
) {
ElMessage.warning("请输入查询条件!");
return;
}
transactionsList.value = [];
let result = await branchWarehouseTransactionsQueryForReagentService(queryObj.value);
transactionsList.value = result.data;
};
......
template>
......
el-button class="btn-same-width" type="primary" plain @click="onQueryClick">查询el-button>
......
template>

一、使用自定义的防止重复点击组合式函数 hook 

1、编写防止重复点击组合式函数 hook,usePreventReClick.ts

2、导入防止重复点击组合式函数 hook

3、使用 preventReClick 防止重复点击

"ts" name="ReagentTransactionsDrawer">
......
import { usePreventReClick } from "@/hooks/usePreventReClick";
const { preventReClick } = usePreventReClick();
// 查询,加装 preventReClick 防抖器,防止重复点击
const onQueryClick = async () => {
preventReClick(async () => { // 改动1:增加防抖代码
// 检查
if (
queryObj.value.transactionTime.length === 0 &&
!queryObj.value.transactionType &&
!queryObj.value.materialName
) {
ElMessage.warning("请输入查询条件!");
return;
}
transactionsList.value = [];
let result = await branchWarehouseTransactionsQueryForReagentService(queryObj.value);
transactionsList.value = result.data;
}); // 改动2:增加防抖代码
};
......
template>
......
el-button class="btn-same-width" type="primary" plain @click="onQueryClick">查询el-button>
......
template>

 防止重复点击组合式函数 hook,usePreventReClick.ts

import { ref } from "vue";
type AsyncFunction = () => Promiseany>;
/**
 * 防止重复点击 hook
 * @returns
 */
export function usePreventReClick() {
const isClicking = ref(false);
const preventReClick = async (fn: AsyncFunction) => {
if (isClicking.value) {
return;
}
isClicking.value = true;
try {
await fn();
} finally {
isClicking.value = false;
}
};
return {
isClicking,
preventReClick
};
}

 二、使用lodash-es的debounce

1、安装 lodash-es 工具库

2、按需使用导入debounce

3、使用 debounce 防止重复点击

"ts" name="ReagentTransactionsDrawer">
......
import { debounce } from "lodash-es";
// 查询,加装 debounce 防抖器,防抖处理(leading: true,立即执行、maxWait: 1000,1秒内至少执行一次)
const onQueryClick = debounce( // 改动1:const onQueryClick = async () => { 改为 const onQueryClick = debounce(
async () => {
// 检查
if (
queryObj.value.transactionTime.length === 0 &&
!queryObj.value.transactionType &&
!queryObj.value.materialName
) {
ElMessage.warning("请输入查询条件!");
return;
}
transactionsList.value = [];
let result = await branchWarehouseTransactionsQueryForReagentService(queryObj.value);
transactionsList.value = result.data;
}, // 改动2:} 改为 },
1000, // 改动3:增加防抖代码
{ leading: true, trailing: true, maxWait: 1000 } // 改动4:增加防抖代码
); // 改动5:增加防抖代码
......
template>
......
el-button class="btn-same-width" type="primary" plain @click="onQueryClick">查询el-button>
......
template>

三、使用自定义防抖组件 

1、编写防止重复点击按钮组件(防抖按钮组件),BasePreventReClickButton.vue

2、导入防抖组件

3、在模板中使用组件

"ts" name="ReagentTransactionsDrawer">
......
import BasePreventReClickButton from "@/components/base/BasePreventReClickButton.vue";
// 查询,使用 BasePreventReClickButton 防抖组件,防止重复点击
const onQueryClick = async () => {
// 检查
if (
queryObj.value.transactionTime.length === 0 &&
!queryObj.value.transactionType &&
!queryObj.value.materialName
) {
ElMessage.warning("请输入查询条件!");
return;
}
transactionsList.value = [];
let result = await branchWarehouseTransactionsQueryForReagentService(queryObj.value);
transactionsList.value = result.data;
};
......
template>
......
BasePreventReClickButton class="btn-same-width" type="primary" plain @click="onQueryClick">查询BasePreventReClickButton>
......
template>

防止重复点击按钮组件(防抖按钮组件),BasePreventReClickButton.vue

/** * 防止重复点击按钮组件(防抖按钮组件) */
"ts" name="BasePreventReClickButton">
import { ref } from "vue";
const props = withDefaults(
definePropsonClick: () => Promisevoid> | void;
delay?: number;
}>(),
{
delay: 0
}
);
// 加载标识
const loading = ref(false);
// 点击事件
const handleClick = async (): Promisevoid> => {
if (loading.value) return;
loading.value = true;
try {
await props.onClick();
} finally {
let delay = props.delay;
if (delay 0) delay = 0;
setTimeout(() => {
loading.value = false;
}, delay);
}
};
template>
el-button v-bind="$attrs" :loading="loading" :disabled="loading" @click="handleClick">
slot>slot>
el-button>
template>
style scoped lang="scss">style>
posted @ 2025-07-24 10:09  yjbjingcha  阅读(95)  评论(0)    收藏  举报