🧩 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