能否无需传递函数Prop重写Vue可复用按钮组件?
改用事件实现Vue可复用执行按钮组件(避免函数Prop反模式)
问题描述
我想开发一个可复用按钮组件AppExecButton,用来封装执行父组件传入逻辑时的加载状态与错误处理代码。当前实现是通过传递函数作为Prop(这属于Vue反模式),附上现有组件代码和父组件使用示例,请问能不能改用事件实现,同时让父组件不用重复编写try/catch和加载状态相关代码?
原AppExecButton组件代码
<script setup lang="ts"> import { ref } from "vue"; import type { ButtonVariant } from "@/components/types"; const props = defineProps<{ onClick: () => Promise<unknown> | unknown; errorMessage?: string; variant?: ButtonVariant; disabled?: boolean; block?: boolean; color?: string; }>(); const loading = ref(false); async function handleClick() { loading.value = true; try { await props.onClick(); } catch (e) { const errorMessage = props.errorMessage; if (errorMessage) { console.error(e); throw new Error(errorMessage); } else { throw e; } } finally { loading.value = false; } } </script> <template> <v-btn @click="handleClick" :loading="loading" :variant="variant" :disabled="disabled" :block="block" :color="color" > <slot /> </v-btn> </template>
原父组件使用示例
<app-exec-btn :on-click="() => downloadNotes()" error-message="Failed to download notes" block variant="outlined" > Download Notes </app-exec-btn>
解决方案
当然可以改用事件机制实现,这既符合Vue单向数据流的设计原则,又能保留组件封装的加载状态管理和错误处理能力。核心逻辑是让子组件触发自定义事件,父组件监听事件并执行业务逻辑,子组件内部负责处理加载状态和错误捕获。
修改后的AppExecButton组件代码
<script setup lang="ts"> import { ref } from "vue"; import type { ButtonVariant } from "@/components/types"; const props = defineProps<{ errorMessage?: string; variant?: ButtonVariant; disabled?: boolean; block?: boolean; color?: string; }>(); // 定义自定义事件,父组件将通过该事件绑定业务逻辑 const emit = defineEmits<{ execute: []; }>(); const loading = ref(false); async function handleClick() { loading.value = true; try { // 触发execute事件,等待父组件的业务逻辑执行完成 await emit("execute"); } catch (e) { const errorMessage = props.errorMessage; if (errorMessage) { console.error(e); throw new Error(errorMessage); } else { throw e; } } finally { loading.value = false; } } </script> <template> <v-btn @click="handleClick" :loading="loading" :variant="variant" :disabled="disabled" :block="block" :color="color" > <slot /> </v-btn> </template>
修改后的父组件使用示例
<app-exec-btn @execute="downloadNotes" error-message="Failed to download notes" block variant="outlined" > Download Notes </app-exec-btn>
关键修改说明
- 移除了原有的
onClickProp,改用execute自定义事件,避免了函数作为Prop的反模式 - 子组件点击时触发
execute事件,父组件通过@execute直接绑定业务函数,无需额外包装箭头函数 - 子组件依然完整保留了加载状态管理和错误捕获逻辑,父组件只需专注于业务逻辑本身,无需重复编写try/catch和加载状态相关代码
内容的提问来源于stack exchange,提问作者Aniruth N
相关产品推荐
相关产品推荐

