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

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

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

相关推荐
无奈何杨2 分钟前
CoolGuard风控中新增移动距离和移动速度指标
前端·后端
恋猫de小郭9 分钟前
Google I/O Extended :2025 Flutter 的现状与未来
android·前端·flutter
江城开朗的豌豆13 分钟前
Vue-router方法大全:让页面跳转随心所欲!
前端·javascript·vue.js
程序员爱钓鱼23 分钟前
Go语言泛型-泛型约束与实践
前端·后端·go
寻月隐君23 分钟前
保姆级教程:Zsh + Oh My Zsh 终极配置,让你的 Ubuntu 终端效率倍增
linux·后端·命令行
前端小巷子25 分钟前
web从输入网址到页面加载完成
前端·面试·浏览器
江城开朗的豌豆25 分钟前
Vue路由动态生成秘籍:让你的链接'活'起来!
前端·javascript·vue.js
程序员爱钓鱼26 分钟前
Go语言泛型-泛型对代码结构的优化
后端·google·go
晓得迷路了26 分钟前
栗子前端技术周刊第 88 期 - Apache ECharts 6.0 beta、Deno 2.4、Astro 5.11...
前端·javascript·echarts
这里有鱼汤31 分钟前
“对象”?对象你个头!——Python世界观彻底崩塌的一天
后端·python