🧩 UI 组件库演进史

一句话定性

UI 组件库的二十年,是一部**“控制权与封装度的钟摆”史:从 Bootstrap 给你全部样式**(封装最高、最省事、也最难改),到 AntD/MUI 给你完整组件(开箱即用、却样式难覆盖、与框架强绑),再到 Headless 只给你最难自己写的那部分(行为 + 状态 + 无障碍,样式还给你),最后到 shadcn/ui 连依赖都不要(把源码复制进你的项目,组件归你)。钟摆每一次回摆,都是把控制权一寸寸还给开发者。


一、为什么需要一部”组件库史”

前端做界面,永远在回答同一个问题:一个”看起来对、点起来对、残障用户也能用”的按钮 / 弹窗 / 下拉框,谁来负责?

这个问题看似琐碎,却牵动整个生态。因为一个”生产级”的组件远比看上去复杂:

一个"下拉菜单"到底有多难

你以为是 <div> 加点击事件。实际生产级 Select 要处理:键盘导航(↑↓ Enter Esc)、焦点管理(focus trap)、点击外部关闭、定位避让(屏幕边缘翻转)、屏幕阅读器朗读(aria-*)、受控/非受控状态、虚拟滚动……真正难的不是样式,是行为、状态和无障碍(a11y)。 这恰恰是后半段历史的主角。

整部演进史的主线,就是社会化分工的不断重切:“难的部分”和”个性化的部分”之间的边界,一次次被重新划定。


二、四个世代:一条主线串起来

世代代表给你什么不给你什么封装度控制权
① CSS 框架Bootstrap (2011)栅格 + 预置 class 样式不管行为/状态(那是 jQuery 的活)中(只给样式皮)样式难改,一眼”Bootstrap 味”
② 框架绑定组件库 Element (2015+)完整组件:样式+行为+状态+a11y不给自由(主题/样式难覆盖)最高(全包)最低(改一点要斗争)
③ Headless / Unstyled React Aria只给行为+状态+a11y故意不给样式低(只给逻辑)样式 100% 归你
④ 复制源码ui (2023)CLI 把组件源码拷进你的仓库不当 npm 依赖,不替你升级零封装(源码即你的)连代码所有权都归你

一句话读懂这张表

从左到右,“别人替你管”的东西越来越少,“你自己说了算”的东西越来越多。 这不是技术退步,而是社会化分工成熟后,把”该外包的(a11y/行为)“外包、把”不该外包的(样式/品牌)“收回——一次精准的责任再分配。


三、钟摆为什么会这样摆?(因果逻辑)

第一摆:从”裸 CSS”到 Bootstrap —— 封装度拉满

jQuery 时代,做页面要手写一切样式。Bootstrap 的洞察很朴素:90% 的网站长得差不多,那就把栅格、按钮、表单、导航全做成预置 class。结果是非设计师也能在一下午做出像样的页面。代价:所有 Bootstrap 站长得像一个模子刻的,且定制要和它的 !important 死磕。

第二摆:从样式皮到完整组件 —— SPA 逼出来的

2013-2018 SPA时代,React/Vue 把 UI 变成组件。Bootstrap 这种”全局 CSS class + 靠 jQuery 加行为”的模型,和组件化范式根本不契合——组件需要的是带行为、带状态、可组合的 React/Vue 组件,而不是一串 class 名。于是 Element 登场:把样式、行为、状态、a11y 全包成一个 <Button>。叠加企业中后台爆发(管理系统对 UI 一致性要求高、对个性化要求低),全包型组件库成为时代标配。

第三摆:Headless —— 把”全包”拆开

全包很爽,直到你要改它。

全包型组件库的两难

  • 想要它的行为和 a11y(这部分极难自己写)→ 必须连它的样式一起吃下;
  • 想要自己的设计(品牌、Design System)→ 又要和它”内置样式”做无尽的覆盖斗争(:deep()!important、改 Less 变量、覆盖 className……)。

两个诉求被焊死在一起,无法分开购买。

Headless-UI 的范式转变正是解耦:Radix、Headless UI、React Aria 只交付行为 + 状态 + a11y(那个最难、最该外包的内核),样式完全留白,交给你用 Tailwind / CSS-in-JS 自由发挥。你既拿到了最难的部分,又夺回了样式控制权。

第四摆:shadcn/ui —— 连依赖都不要

Headless 解决了”样式归你”,但组件仍是 node_modules 里的依赖:你不能改它的内部逻辑,升级可能 break,定制只能在它给的口子里。

shadcn-ui 走到逻辑终点:不发 npm 包,用 CLI 把组件源码直接拷进你的 components/ 目录。组件成了你仓库里的普通文件——可以随便改、不会被升级偷换、没有版本锁定。它建立在 Radix(行为/a11y)+ Tailwind(样式)之上,把”控制权回归”推到极致:连代码所有权都还给你。 这个”反依赖”思路在 AI 时代 尤其契合——AI 改你仓库里的源码,比改 node_modules 里别人的依赖顺手得多(见 AI-Native-Development)。


四、贯穿主线:控制权钟摆图

封装度 高 ◄──────────────────────────────────────► 低
控制权 低 ◄──────────────────────────────────────► 高

[Bootstrap 2011]        给"全部样式"          一眼 Bootstrap 味, 难改
   jQuery 时代           ↓ SPA 组件化, 全局 CSS 不契合
[AntD/MUI/Element 2015]  给"完整组件"(样式+行为+状态+a11y)
   SPA / 中后台爆发       ↓ 样式难覆盖 + a11y 自己写太难, 但两者被焊死
[Headless 2019+]        只给"最难的"(行为+状态+a11y), 样式留白
   Radix/HeadlessUI      ↓ 配合 Tailwind 自由出样式; 但组件仍是 npm 依赖
[shadcn/ui 2023]        连依赖都不要 —— CLI 把源码复制进你的项目
   AI 时代               ↓ 源码归你, 无版本锁定, AI 易改
                        ►► 控制权完全回归开发者

钟摆的方向是有意义的

每一次回摆都精准地”还”了一样东西:Headless 还的是样式控制权,shadcn 还的是代码所有权。而它们没有还的,恰恰是那些真该外包的——行为、状态、a11y 内核始终由 Radix/React Aria 这类专业项目托管。这说明成熟的不是”什么都自己写”,而是知道哪些该外包、哪些该收回


五、各篇导航

  • 🅱️ Bootstrap —— CSS 框架鼻祖,封装度起点,“一眼 Bootstrap 味”的得与失
  • 🐜 组件库时代-AntD-MUI-Element —— 全包型组件库与 Design System 概念的兴起
  • 🧠 Headless-UI —— 行为/a11y 与样式的解耦,关键范式转变
  • 📋 shadcn-ui —— “组件库即依赖”二十年假设的终结

🔗 时代:2005-2013 Ajax时代 | 2013-2018 SPA时代 | 2018-2023 工程化时代 | 2023-未来 AI时代 🔗 框架:React | Vue | jQuery | Angular-2+ 🔗 样式:原子化CSS | CSS-in-JS | CSS演进史 🔗 生态:为什么Vue在中国崛起 | AI-Native-Development