简介
vue3中使用TSX(JSX)语法二次封装函数式loading-button组件,及其单文件loading-button与函数式loading-button的简单对比,这里我使用vant组件库的button作为二次封装的示例,实际项目中可以根据自己的需求来封装。
一、单文件组件 loading-button
1.代码
<template>
<van-button v-bind="$attrs" :loading="loading" @click="loadingClick"
><slot></slot
></van-button>
</template>
<script setup lang="ts">
/**
* @description: 具有loading状态的按钮
* @param {Function} loadingClick 点击事件,传入原有click事件触发的方法即可
* @param {https://vant-contrib.gitee.io/vant/#/zh-CN/button} 支持其中所有的属性
*/
import { ref } from "vue";
const props = defineProps({
loadingClick: {
type: Function,
default: () => Promise<void>
}
});
// 按钮loading状态
const loading = ref(false);
async function loadingClick() {
try {
loading.value = true;
await props.loadingClick();
loading.value = false;
} catch (error) {
console.error(error);
loading.value = false;
}
// 自定义的点击事件处理逻辑
}
</script>
<style scoped lang="scss"></style>
2.使用示例
<template>
<div>
<LoadingButton :loadingClick="fatherClickHandler">点击</LoadingButton>
</div>
</template>
<script setup lang="ts">
import LoadingButton from "@/components/LoadingButton/LoadingButton.vue";
async function fatherClickHandler() {
await new Promise(resolve => {
console.log("fatherClickHandler---点击事件开始");
setTimeout(() => {
console.log("fatherClickHandler---点击事件结束");
resolve("点击事件完成");
}, 2000);
});
}
</script>
<style lang="less" scoped></style>
二、函数式组件 loading-button
1.代码
import { defineComponent, ref } from "vue";
import { Button } from "vant";
/**
* @description: 具有loading状态的按钮,传入的方法为为异步方法时自动添加loading状态
* @param {https://vant-contrib.gitee.io/vant/#/zh-CN/button} 支持其中所有的属性
*/
export default defineComponent(
(props, { attrs, slots }) => {
// 就像在 <script setup> 中一样使用组合式 API
const loading = ref(false);
const clickHandler = async () => {
try {
loading.value = true;
// onClick事件没传递时,不执行await
props.onClick && (await props.onClick());
loading.value = false;
} catch (error) {
console.error(error);
loading.value = false;
}
};
return () => {
return (
<>
<Button
type="default"
{...attrs}
onClick={clickHandler}
loading={loading.value}
>
{slots.default?.() || "按钮"}
</Button>
</>
);
};
},
{
// 这里需要注意的是需要声明接收onClick属性
props: ["onClick"]
}
);
2.使用示例
<template>
<div>
<FunLoadingBtn @click="fatherClickHandler">点击</FunLoadingBtn>
</div>
</template>
<script setup lang="ts">
import FunLoadingBtn from "@/components/FunLoadingBtn/FunLoadingBtn";
async function fatherClickHandler() {
await new Promise(resolve => {
console.log("fatherClickHandler---点击事件开始");
setTimeout(() => {
console.log("fatherClickHandler---点击事件结束");
resolve("点击事件完成");
}, 2000);
});
}
</script>
<style lang="less" scoped></style>
三、使用效果
单文件组件和函数时组件使用效果相同。
四、分析
1.二者封装思路对比?
由上面的代码可以看出:
在单文件组件中,是额外定义了一个loadingClick参数来实现这个loading效果的,每次在父组件中使用,我们都需要通过这个自定义参数来实现,也就是说在使用中,相对于vant组件库中van-button的参数,我们还需要时刻牢记loadingClick这个参数,这样无疑给我们带来了额外的负担
而在函数式组件中,我们是直接通过@click进行实现的,在父组件使用的时候,我们使用函数式组件,没有任何使用负担,直接按照vant组件库文档进行使用就行。
2.为什么单文件组件不能直接使用@click进行封装呢,这样不也就在形式和使用上没有任何使用负担了吗?
在vue3.0文档中有一句这个话
或者你可以通过原生的方式拦截loading-button组件内部的onClick执行,并且将父组件的onClick事件在子组件内部执行(听起来就麻烦)