封装 Vue 组件时 props 怎么设计才更灵活?
我最近在封装一个通用的按钮组件,想让它既能支持普通点击,又能传入自定义图标和样式。但每次加新功能就得改 props 结构,感觉很僵硬。比如现在这样写:
props: {
type: { type: String, default: 'primary' },
icon: { type: String, default: '' },
disabled: { type: Boolean, default: false }
}
如果后面要加 loading 状态或者 size 属性,是不是得不停往 props 里堆?有没有更优雅的方式,比如用对象传参或者组合式 API 来提升扩展性?
核心问题不是 props 堆不堆,而是「哪些是组件内部逻辑,哪些是外部可控的配置」没分清。比如
loading、size这些,真得全塞进 props 吗?不一定。我建议分三层来设计:
第一层:基础交互类,比如
disabled、loading,这类状态是组件自己维护的,但需要外部控制,可以保留为 props。第二层:视觉样式类,比如
type、size、icon,这类其实可以拆成class+style+slots来组合控制,比硬编码 props 灵活多了。第三层:内容定制类,比如图标、默认插槽内容,直接用
slot或v-slot,别硬塞成字符串。举个具体例子,你这个按钮可以这样改:
props: {
disabled: Boolean,
loading: Boolean,
// type 改成 class 前缀,或者直接让用户传 class
// size 也一样,或者直接用 class 控制
}
然后模板里:
:class="[
'btn',
type &&
btn-${type},size &&
btn-${size},loading && 'btn-loading',
disabled && 'btn-disabled'
]"
:disabled="disabled || loading"
外部调用的时候:
提交
或者更狠点,连
type、size都不走 props,直接让用户传class:你还可以结合
useAttrs(Vue3)把没声明的 attrs 全透传下去,比如title、aria-label这些,也不用一个个声明 props。最后说句实话,如果你发现某个 prop 每次传的值都是固定几个,比如
['small', 'medium', 'large'],那确实该加个 props;但如果每次都是拼出来的 class 名或者自定义值,不如直接让用户传 class 或 style,省心。调试看看你现在的 props 列表,哪些是组件必须知道的逻辑状态,哪些只是样式变量,拆开后你会轻松很多。