❌「不要再封装组件了!」我把 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 项目中快速构建高一致性视觉体验。

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

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

相关推荐
kidding7236 分钟前
微信小程序怎么分包步骤(包括怎么主包跳转到分包)
前端·微信小程序·前端开发·分包·wx.navigateto·subpackages
Huazie7 分钟前
在WSL2 Ubuntu中部署FastDFS服务的完整指南
服务器·后端·ubuntu
微学AI20 分钟前
详细介绍:MCP(大模型上下文协议)的架构与组件,以及MCP的开发实践
前端·人工智能·深度学习·架构·llm·mcp
liangshanbo12151 小时前
CSS 包含块
前端·css
Mitchell_C1 小时前
语义化 HTML (Semantic HTML)
前端·html
倒霉男孩1 小时前
CSS文本属性
前端·css
shoa_top1 小时前
JavaScript 数组方法总结
javascript
行者无疆xcc1 小时前
【Django】设置让局域网内的人访问
后端·python·django
晚风3081 小时前
前端
前端
JiangJiang1 小时前
🚀 Vue 人如何玩转 React 自定义 Hook?从 Mixins 到 Hook 的华丽转身
前端·react.js·面试