组件封装的一些思考
提示本文发布于 5 个月前,其中信息可能已经时过境迁...
做前端越久,越觉得"封装"这两个字说起来容易,做起来难。把功能写出来不难,但写成一个别人也能轻松用、改、扩的组件,又是另一回事。
最近整理笔记,把组件封装的一些经验沉淀了下来。
封装的几个维度
1. 通用性
组件不能太"业务"。如果一个组件里写死了某个接口的字段名、某个特定页面的样式,那它就不算一个通用组件。
我现在的习惯是:先抽象出"这个组件最核心的能力是什么",再围绕核心能力设计接口。业务相关的逻辑通过 props/slots 让调用方自己去处理。
2. API 清晰
一个好的组件,看 props 定义就应该知道怎么用。类型定义、默认值、注释,这些看似不起眼的东西,在多人协作的时候作用很大。
typescript
const props = withDefaults(defineProps<{
// 数据源
data: Array<any>;
// 是否显示加载状态
loading?: boolean;
// 空数据时的提示文案
emptyText?: string;
}>(), {
loading: false,
emptyText: '暂无数据',
});3. 属性穿透
二次封装第三方组件时,最头疼的是怎么把属性和事件传过去。Vue 3 的 $attrs 可以自动把未声明的属性传递给子组件,但插槽和方法暴露就需要手动处理了。
插槽穿透可以用 h 函数:
vue
<template>
<component :is="h(ElInput, $attrs, $slots)"></component>
</template>方法暴露可以用 ref 回调的方式:
vue
<template>
<component :is="h(ElInput, { ...$attrs, ref: exposeRef }, $slots)"></component>
</template>
<script setup>
function exposeRef(exposed) {
const instance = getCurrentInstance();
instance.exposed = exposed;
}
</script>这样二次封装的组件,对调用方来说是"透明的"——用起来和直接用原组件没什么区别。
封装的意义
封装不是为了炫技,而是为了"隔离复杂度"。调用方只需要知道"这个组件能做什么",不需要知道"它怎么做的"。
把复杂留给自己,把简单留给别人——这是我理解的封装精神。
