vue3中使用TSX(JSX)语法二次封装函数式loading-button组件,及其单文件loading-button与函数式loading-button对比

928 阅读2分钟

简介

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>

三、使用效果

单文件组件和函数时组件使用效果相同。 20230904_110150.gif

四、分析

1.二者封装思路对比?

由上面的代码可以看出:
单文件组件中,是额外定义了一个loadingClick参数来实现这个loading效果的,每次在父组件中使用,我们都需要通过这个自定义参数来实现,也就是说在使用中,相对于vant组件库中van-button的参数,我们还需要时刻牢记loadingClick这个参数,这样无疑给我们带来了额外的负担
而在函数式组件中,我们是直接通过@click进行实现的,在父组件使用的时候,我们使用函数式组件,没有任何使用负担,直接按照vant组件库文档进行使用就行。

2.为什么单文件组件不能直接使用@click进行封装呢,这样不也就在形式和使用上没有任何使用负担了吗?

在vue3.0文档中有一句这个话

图片.png 或者你可以通过原生的方式拦截loading-button组件内部的onClick执行,并且将父组件的onClick事件在子组件内部执行(听起来就麻烦