Vue3封装组件:el-button加载状态管理

问题背景:

在实际前端开发中,当页面包含多个按钮时,代码往往会出现大量重复的加载状态管理逻辑,不仅会造成代码冗余,而且会导致遗漏状态或异常处理不完善

<template>
  <el-button :loading="loading1" @click="onClick1">按钮1</el-button>
  <el-button :loading="loading2" @click="onClick2">按钮2</el-button>
  <el-button :loading="loading3" @click="onClick3">按钮3</el-button>
</template>

<script setup lang="ts">
import { ref } from 'vue'
// 每个按钮都需要独立的loading状态变量
const loading1 = ref(false)
const loading2 = ref(false)
const loading3 = ref(false)
// 每个点击事件都需要重复的loading状态管理逻辑
async function onClick1() {
  loading1.value = true
  try {
    // 执行具体业务逻辑
    await fetchData1()
  } finally {
    loading1.value = false
  }
}
function onClick2() {/**省略*/}
function onClick3() {/**省略*/}
</script>
解决方案:

通过封装一个智能的 MyButton 组件,可以自动处理加载状态,简化代码:

<template>
  <!-- 使用方式简化,只需传递点击事件 -->
  <my-button @click="onClick1">按钮1</my-button>
  <my-button @click="onClick2">
    按钮2
    <template #loading>提交中...</template>
</my-button>
  <my-button @click="onClick3">按钮3</my-button>
</template>

<script setup lang="ts">
// 业务逻辑更纯粹,无需关心loading状态
async function onClick1() {
  await fetchData1()
}

function onClick2() {/**省略*/}
function onClick3() {/**省略*/}
</script>
组件实现代码
<template>
  <el-button 
    v-bind="filteredAttrs" 
    :loading="loading" 
    @click="handleClick"
  >
    <!-- 支持自定义加载状态文本 -->
    <template v-if="loading && $slots.loading">
      <slot name="loading"></slot>
    </template>
    <!-- 支持自定义按钮内容 -->
    <slot></slot>
  </el-button>
</template>

<script setup lang="ts">
import { ref, useAttrs, computed } from 'vue'

// !禁用默认的属性继承,避免属性和事件重复绑定到根元素
defineOptions({
  inheritAttrs: false
})

// 获取父组件传递的所有属性和事件(除了在props中声明的)
const attrs = useAttrs()

// 计算属性:过滤掉 onClick 事件,避免重复绑定
 const filteredAttrs = computed(() => {
  const { onClick, ...rest } = attrs
  return rest
})

const loading = ref<boolean>(false)

/**
 * 处理按钮点击事件
 * 自动管理loading状态,并执行父组件传递的onClick方法
 */
async function handleClick() {
  loading.value = true
  try {
    // 执行父组件传递的点击事件处理函数(支持异步操作)
    await (attrs.onClick as (() => Promise<void> | void))?.()
  } catch (error) {
    console.error('按钮操作执行失败:', error)
    // 可选:向上抛出错误,让父组件处理
    // throw error
  } finally {
    loading.value = false
  }
}
</script>

链接汇总

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容