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

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

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

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

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

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


🤯 封装组件的幻觉

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

ini 复制代码
<MyButton
  type="primary"
  variant="ghost"
  color="red"
  theme="dark"
  danger
  size="small"
/>

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

问题包括:

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

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

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

arduino 复制代码
// 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'
  )
}

使用方式:

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

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


🧩 真实项目效果对比

💢 之前:

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

✨ 现在:

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

🌈 弹窗、输入框一样适用

css 复制代码
{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 项目中快速构建高一致性视觉体验。

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

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

相关推荐
掘金者阿豪41 分钟前
关系数据库迁移的“暗礁”:金仓数据库如何规避数据完整性与一致性风险
后端
m0_7190841144 分钟前
React笔记张天禹
前端·笔记·react.js
ServBay1 小时前
一个下午,一台电脑,终结你 90% 的 Symfony 重复劳动
后端·php·symfony
Ziky学习记录1 小时前
从零到实战:React Router 学习与总结
前端·学习·react.js
sino爱学习1 小时前
高性能线程池实践:Dubbo EagerThreadPool 设计与应用
java·后端
wuhen_n1 小时前
JavaScript链表与双向链表实现:理解数组与链表的差异
前端·javascript
wuhen_n1 小时前
JavaScript数据结构深度解析:栈、队列与树的实现与应用
前端·javascript
我是一只puppy1 小时前
使用AI进行代码审查
javascript·人工智能·git·安全·源代码管理
颜酱1 小时前
从二叉树到衍生结构:5种高频树结构原理+解析
javascript·后端·算法
掘金者阿豪1 小时前
UUID的隐形成本:一个让数据库“慢下来”的陷阱
后端