如何设计一个“真正可复用”的前端组件?

3,703 阅读3分钟

🧱 如何设计一个“真正可复用”的前端组件?

🔧 一个按钮可以写10次,也可以封装一次复用全场;组件是前端的积木,而设计模式才是组装它们的说明书。你真的在写“可复用”组件吗?


🧠 什么是可复用组件?

所谓“可复用”,不仅是复制粘贴组件这么简单,而是:

✅ 能跨多个页面/模块使用 ✅ 能被复合、嵌套、组合 ✅ 能支持灵活配置和扩展 ✅ 能隔离内部逻辑,隐藏实现细节 ✅ 能在多个项目中迁移使用(库化)


🧩 核心原则一:面向配置编程(Props 设计)

以一个弹窗组件为例:

<Modal
  title="提示"
  visible={visible}
  onClose={() => setVisible(false)}
  footer={<CustomFooter />}
/>

✅ 好的 Props 应该:

特性示例
🧠 可读性强title="登录提示"t="登录"
🧩 支持组合footer 支持传 JSX 或隐藏
🎯 默认值合理closable=true 不传也能用
🚀 高扩展性beforeClose() 钩子支持拦截关闭

🧩 核心原则二:关注点分离(拆 UI 和逻辑)

避免在组件中同时处理:

  • UI 渲染
  • 状态管理
  • 数据获取
  • 事件逻辑

✅ 推荐:UI 组件 + Hook/Composable 分离逻辑

React 示例(Hooks):

function useModal() {
  const [visible, setVisible] = useState(false);
  const open = () => setVisible(true);
  const close = () => setVisible(false);
  return { visible, open, close };
}

组件层只负责渲染:

<Modal visible={visible} onClose={close} />

Vue 示例(Composable):

export function useModal() {
  const visible = ref(false);
  const open = () => (visible.value = true);
  return { visible, open };
}

🧩 核心原则三:插槽 & Render Props 让组件更灵活

Vue 中使用 slot

<CustomTable>
  <template #toolbar>
    <Button>新增</Button>
  </template>
</CustomTable>

React 中使用 children 或 render props:

<Table
  renderToolbar={() => (
    <Button>新增</Button>
  )}
/>

插槽 / Render Props 是组件复用的“开放式接口”。


🧩 核心原则四:可组合的组件设计(组合 > 继承)

一个“组合式”的 Form 组件架构 👇

<Form>
  <Form.Item label="用户名">
    <Input />
  </Form.Item>
  <Form.Item label="密码">
    <Input type="password" />
  </Form.Item>
</Form>
  • Form 管理上下文(如验证、收集值)
  • Form.Item 管理 label、错误提示
  • Input 专注输入逻辑

优点:

  • ✅ 结构清晰
  • ✅ 容易插拔/扩展
  • ✅ 内聚逻辑解耦封装

🔧 技术加持:让组件更强大的方法

技术作用
TypeScript 泛型精确 props 类型,支持 IDE 提示
事件派发 / 自定义事件组件内向外传递操作
Context / Provide/Inject实现跨层通信
组合式函数(Hooks/Composables)提取公共逻辑
主题系统 / 样式变量实现定制化风格

🧪 示例:一个「超可复用」的 Table 组件设计思路

✅ 配置式 + 插槽:

<Table
  columns={[
    { title: '名称', dataIndex: 'name' },
    { title: '年龄', dataIndex: 'age', render: val => <Tag>{val}</Tag> },
  ]}
  dataSource={data}
/>

✅ 扩展:支持分页、loading、自定义操作栏、空状态提示等:

<Table
  columns={columns}
  dataSource={data}
  loading={loading}
  pagination={{ current: 1, pageSize: 10 }}
  emptyTip={<Empty description="暂无数据" />}
/>

这样封装后,整套表格系统可被复用于几十个页面。


⚠️ 不可复用组件的常见“反模式”

反模式问题
Props 拆得太碎使用复杂,传值不直观
写死业务逻辑拖不动、迁不走、复用不了
没有状态控制出口用户无法控制内部行为
样式不隔离每次引入就“爆炸”
接口数据写死在组件里不通用,只能在一个项目用

🧠 总结:可复用组件的五个关键特性

  1. 低耦合:功能单一,职责清晰
  2. 高内聚:UI 与状态适度绑定
  3. 可组合:支持嵌套、插槽、自定义结构
  4. 可配置:props + config 驱动行为
  5. 可扩展:暴露必要事件、方法,便于集成与增强

组件写得好,维护效率能提升一个维度;写得乱,整个项目都跟着混乱。


👍 如果你觉得这篇文章有帮助,欢迎点赞、关注、收藏,下一篇为大家带来《如何打造一个通用表单生成器组件系统》