Reader

❌「不要再封装组件了!」我把 UI 重构成了纯函数式

| 掘金本周最热 | Default

❌「不要再封装组件了!」我把 UI 重构成了纯函数式

“组件封装写多了,有没有一种感觉:你根本不知道你自己封了个啥。”

我之前在项目里封了 60+ 个 UI 组件,按钮就有三种:Button、MyButton、SubmitButton,结果样式越来越乱,props 越来越多,调样式就像打仗一样。

后来我决定把整个 UI 层彻底重构为函数式

这篇文章我来讲讲:为什么不再封装组件,而是用 Tailwind + 纯函数构建 UI,并分享我实际项目中的改造过程。


🤯 封装组件的幻觉

我们都觉得“封装组件=规范”,但实际情况呢?

<MyButton
  type="primary"
  variant="ghost"
  color="red"
  theme="dark"
  danger
  size="small"
/>

看着像规范,其实已经成为“props 分布式样式控制中心”。

问题包括:

  • 样式靠 props 判断,逻辑越来越臃肿
  • 组件之间样式不一致,改一个按钮影响一堆地方
  • 想换个主题,得重构全部组件
  • 甚至某些 props 决定逻辑 + UI 的组合,根本不好维护

✅ 我怎么改的?直接函数式构建样式

我保留最基础的组件(如 Button 用 ),其他一律改成纯函数样式生成:

// ui/button.ts
export function useButtonClass({
  variant = 'primary',
  ghost = false,
  size = 'base',
}: {
  variant?: 'primary' | 'danger' | 'default'
  ghost?: boolean
  size?: 'base' | 'sm' | 'lg'
}) {
  return cn(
    'inline-flex items-center justify-center font-medium rounded transition',
    size === 'sm' && 'px-2 py-1 text-sm',
    size === 'lg' && 'px-5 py-3 text-lg',
    size === 'base' && 'px-4 py-2 text-base',
    variant === 'primary' && 'bg-blue-600 text-white hover:bg-blue-700',
    variant === 'danger' && 'bg-red-500 text-white hover:bg-red-600',
    ghost && 'bg-transparent border hover:underline'
  )
}

使用方式:

<button className={useButtonClass({ variant: 'danger', ghost: true })}>
  删除
</button>

纯净、语义清晰、无副作用。


🧩 真实项目效果对比

💢 之前:

<MyButton type="danger" ghost size="small" onClick={handleDelete}>
  删除
</MyButton>
  • 组件需要解构 props 做判断
  • 样式嵌套多层,覆盖麻烦
  • 动态变更样式要改组件内部逻辑

✨ 现在:

<button
  className={useButtonClass({ variant: 'danger', ghost: true, size: 'sm' })}
  onClick={handleDelete}
>
  删除
</button>
  • 样式全部在 class 中生成
  • 完全由调用方控制,无副作用
  • 支持 tailwind 动态主题、响应式无痛切换

🌈 弹窗、输入框一样适用

{showModal && (
  <div className={modalClass()}>
    <h2 className="text-lg font-bold">确认删除?</h2>
    <div className="flex gap-2 mt-4">
      <button className={useButtonClass({ variant: 'danger' })}>确认</button>
      <button className={useButtonClass({ variant: 'default', ghost: true })}>
        取消
      </button>
    </div>
  </div>
)}

组件自由组合,样式不依赖“封装逻辑”。


🚫 不适合的情况

不是所有场景都推荐用函数式样式替代组件封装:

场景推荐方式
小按钮、输入框、表单项等基础控件✅ 函数样式
富交互组件(如 Drawer、DatePicker)✅ 组件封装
行为逻辑重(如权限/校验/联动)✅ 封装组件组合

我的做法是:最小颗粒度用函数封装样式,复杂交互用组件包逻辑


📦 补充工具推荐

  • clsx / classnames:动态 class 拼接
  • tailwind-variants:类函数式样式生成器(更高级)
  • tailwindcss-animate:添加统一动画支持

✅ 总结:组件 ≠ 万能解药

React 本质上是 UI = f(state)。封装组件容易让我们忽视状态和样式之间的映射关系。

样式函数就是在做真正的“UI 映射” :不同参数 → 不同视觉 → 统一逻辑。

组件是树,样式是数据,UI 是表达。

函数式构建样式,是表达力与灵活性的平衡。


🧩 项目预告

我正在封装一套完整的「函数式样式 UI 系统」,可用于 React + Tailwind 项目中快速构建高一致性视觉体验。

欢迎点赞 + 收藏支持我继续更新 ✨

如果你也想试试这种写法,评论区一起交流!