一个很常见的实际场景,form表单提交的按钮需要防重提交,不然数据库就2条数据,怎么通过组件级别来控制呢?正常按钮都有点击事件,并且都是promise,我们能否拦截click,等promise结束,再设置loading为false。element-ui是一个优秀的vue组件,以这个为例书写一个el-pro-button
查看源码
import { ref, defineComponent } from "vue";
function useClick(onClick: any) {
const loading = ref(false);
const isPromise = (obj: any) => {
// eslint-disable-next-line @typescript-eslint/no-empty-function
const AsyncFunction = (async () => {}).constructor;
return obj instanceof Promise || obj instanceof AsyncFunction === true;
};
const handleClick = () => {
const fn = onClick;
if (!fn || loading.value) {
return;
}
loading.value = true;
const execFn = fn.call(null, event);
if (isPromise(execFn)) {
execFn.finally(() => {
loading.value = false;
});
} else {
// 延迟300ms,关闭loading
setTimeout(() => {
loading.value = false;
}, 300);
}
};
return {
loading,
handleClick,
};
}
export default defineComponent({
props: {
onClick: Function,
},
setup(props, { slots, attrs }) {
const { loading, handleClick } = useClick(props.onClick);
const children = slots && slots.default && slots.default();
return () => (
<el-button {...attrs} loading={loading.value} onClick={handleClick}>
{children}
</el-button>
);
},
});
调用方法
<script lang="ts" setup>
import ElProButton from "./components/el-pro-button";
const sleep = (timer: number) =>
new Promise((resolve) => setTimeout(resolve, timer));
const handleClick1 = async () => {
await sleep(1000);
};
</script>
<template>
<h2>普通按照展示</h2>
<el-pro-button type="primary">test</el-pro-button>
<h2>按钮带事件</h2>
<el-pro-button type="primary" @click="handleClick1">test</el-pro-button>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
}
</style>