Unocss 笔记
约 3330 字大约 11 分钟
2026-02-05
unocss 和 tailwind 同属“原子化 CSS(utility-first)”路线,但不是同一个东西,Tailwind 是“成品框架”,UnoCSS 更像“可插拔的引擎 / 生成器”。
写 p-4 text-center bg-red-500 这种类名,它们都会把对应 CSS 生成出来(或提供出来),目的都是:少写自定义 CSS、靠组合类名完成样式。
不同点:Tailwind 是一套固定体系;UnoCSS 更灵活
Tailwind
- 自带一整套规则、设计系统、默认主题(spacing、colors、breakpoints…)
- 生态成熟(插件、UI 库、社区范式)
- 基本按它的“语法/约定”用
UnoCSS
本质是一个“按需生成 CSS 的引擎”(很轻量)
可以选择预设(preset)来模拟不同体系:
presetUno:UnoCSS 自己的默认规则presetAttributify:可以写属性化的p="4" text="center"presetWind:提供 Tailwind-like 的规则(很多类名兼容 Tailwind)更强调“你想要什么语法/规则,我就生成什么”,可定制空间更大
想用 Tailwind 的类名体系 + 更快更按需 + 语法更自由 → 可能更偏 UnoCSS
想要成熟的默认设计系统 + 大量现成教程/组件生态 → Tailwind 更省心
一个好记的比喻
- Tailwind:一整套装修风格 + 工具箱 + 使用手册
- UnoCSS:一个超快的工具箱引擎,你可以装 Tailwind 风格的刀头,也可以装别的
“UnoCSS 当底座 + 装 Tailwind 刀头(presetWind)”
使用体验:
- 类名习惯基本不变:
flex,p-4,text-center,bg-red-500这类 Tailwind 风格照写 - 更轻、更按需:只生成你项目里实际用到的 utility(而不是一整套预构建)
- 更自由:可以混用 UnoCSS 的一些增强玩法(比如 attributify、shortcuts、自定义规则)
- 更可定制:想加规则就加规则,不必完全受 Tailwind 体系限制
presetWind 覆盖了大量常用类,但 Tailwind 的某些细节、插件生态(比如某些第三方 Tailwind 插件)不一定能原样搬过来,如果你项目强依赖 Tailwind 插件/生态组件,Tailwind 本体可能更稳。
“写 Tailwind 的 class,但希望更轻量 + 更自由定制 + 极致按需生成”,那 UnoCSS + presetWind 就是很典型的组合。
核心论证
- 原子化 CSS 的关键矛盾不是“要不要 utility”,而是 生成策略与可定制性 如何匹配现代开发(尤其是 Vite 这类超快工具链)。
- “全量生成 + 事后清理(Purge)”在开发期会成为性能瓶颈;“按需生成(JIT / on-demand)”能从根上解决。
- 但 Tailwind/Windi 在“按需生成”之后,暴露出另一个痛点:直觉性与定制能力 仍被历史包袱限制(配置/插件体系还停留在旧时代思维)。
- UnoCSS 的定位不是再造一个框架,而是把“原子化 CSS”抽象成一个 高性能、极灵活的即时生成引擎:规则、变体、预设全可组合,甚至能“模拟多个框架”或创造新框架。
原子化 CSS 定义与范围
原子化 CSS:倾向于 小、单一用途 的 class,命名基于视觉功能(例如 margin、颜色等),也常被叫做 functional CSS / utility classes。
文章不争论“原子化 CSS 值不值得用”,而是从框架作者视角讨论:如何权衡设计、有哪些局限、怎样做得更好。
为什么重构
- 作者(Anthony Fu)在 Vite 生态里做模板 Vitesse,默认用 Tailwind,享受原子化 CSS 的开发效率。
- 但在 Vite 这种加载/更新极快的工具链里,Tailwind 传统模式生成的 数 MB CSS 反而成为性能瓶颈(尤其是开发期的加载与更新)。
- 发现 Windi CSS:零依赖(不强制 PostCSS/Autoprefixer),更重要是支持 按需生成,并且作者做了 Vite 插件,性能可到 Tailwind 的 20~100 倍量级。
- Windi 团队也做了很多创新(自动值推导、变体组、Shortcuts、DevTools 设计、属性化模式等),并影响 Tailwind 推出 JIT 引擎。
剖析原子化 CSS 的生成方式:传统 vs 按需
传统方案:全量生成(再配合 Purge)
- 传统做法是:预先生成“你可能会用到的所有工具类”(例子:用 SCSS 循环生成
.m-1到.m-10)。 - 问题一:覆盖范围有限(只生成了 1~10 就没法直接用 11、100 之类)。
- 问题二:体积爆炸:加方向(mt/mb/ml/mr…)×5,加伪类(hover/focus…)更膨胀,最后变成“数 MB CSS”。
- Tailwind 用 PurgeCSS 在生产构建扫描产物、删除没用的规则,把生产 CSS 压到几 KB;但注意:开发期仍要加载全量巨大 CSS,在 Vite 下影响非常明显。
按需生成:把“扫描”和“生成”顺序对调
- 核心思想:先扫描你源码里实际用到的 class,再生成对应 CSS,避免生成无用内容,同时也允许更动态、更自由的值。
- Windi/Tailwind JIT 常见实现:启动前先 glob 扫文件,把内容交给生成器;开发期再用文件监听器(chokidar)做增量扫描并触发 HMR。
- 结果:按需生成能让 Windi 相对传统 Tailwind 速度提升巨大(作者给出约 100 倍级别的说法)。
按需有了,但体验仍会被打断
作者非常强调一个“体感问题”:理想的原子化 CSS 应该是 直觉性的,一旦学会规则,你就能推导出其他用法;否则工具会频繁打断你的心流。
直觉性被破坏的例子:border-10 为什么不生效?
- 你知道
border-2/4/6/8对应 px,但border-10突然不工作,容易让人困惑。 - 你可以“自己写个 .border-10”解决,但这违背了使用原子化框架的初衷。
- 正统做法是去 Tailwind 配置里把
borderWidth: { 10: '10px' }加进去。问题在于:你为了一个小缺口被迫查文档、改配置、再回到原任务,上下文被打断。
Windi 更宽松,但“兼容包袱”让深度定制仍痛苦
- Windi 会尽量推断,让
border-10开箱即用;但为了和 Tailwind 兼容,它仍沿用 Tailwind 的同款配置体系。 - 真正的噩梦出现在“加自定义工具类”时:Tailwind 插件示例里为了定义 rotate 的 utilities,需要写一长串 JS(甚至比生成的 CSS 还长),难读难维护,还破坏按需的灵活性。
- 作者结论:Tailwind 的 API/插件系统沿用旧思维(为全量生成时代设计),不适配新的随需应变(on-demand)理念,定制化能力也有限。
重新设计
定位:UnoCSS 是“引擎”不是“框架”
- UnoCSS 不自带固定的一套核心工具类;能力由 预设(presets)+ 内联配置 提供。
- 目标是:用预设去模拟大多数现有原子化框架,甚至让你用它做“你自己的原子化框架”。(文中举了未来可能有 bootstrap/tachyons/tailwind/windi 等 preset 的想象。)
直观且完全可定制:规则(rules)要“几秒钟就能写出来”,作者的设计点:定义规则要足够直接、可读、可维护。
静态规则,例:['m-1', { margin: '0.25rem' }],检测到 class 就生成对应 CSS。
动态规则(正则 + 函数)
- 例:
/^m-(\d+)$/把数字捕获出来,计算d/4 rem,于是m-100能直接生成margin: 25rem。 - 这种机制意味着:你不需要把“所有可能值”写进配置表里,而是用规则自然推导;也更符合作者追求的“直觉性”。
UnoCSS 的 variants 设计同样是“匹配 + 改写”的小模块:
- 示例:统一支持
hover:前缀,把选择器改成:hover。 - 示例:支持
!前缀,通过 rewrite 给所有值加!important。
重点:variants 不是写死在框架里,而是你可以自己定义、扩展,并作用于你自定义的任何规则。
Presets:把规则/variants 打包复用;默认 preset 是“通用超集”
- 你可以把自定义规则与 variants 打包成 preset 分享,或用 UnoCSS 作为底座创造新的“框架层”。
- 作者特别提到默认的
@unocss/preset-uno(当时实验阶段)是多个流行框架的“通用超集”:Tailwind/Windi/Bootstrap/Tachyons 等的多种写法能同时生效(如ml-3,ms-2,ma4,mt-10px)。
灵活性展示 1:属性化模式(Attributify)
- Windi 的属性化模式被作者认为是很受欢迎的特性:用属性对 utility 做分组,减少重复前缀,提升可读性。
- 原文示例:把一长串 class 拆到
bg="" text="" font="" p="" border=""等属性里,还能组合hover:、dark:。 - UnoCSS 的实现重点:只用 一个 variant + 一个 extractor,总共不到 100 行,并且 天然支持你的自定义规则。
- 甚至还能支持“无值属性”:
<div m-2 rounded text-teal-400 />这种更短的写法。 - 此能力由
@unocss/preset-attributify提供。
灵活性展示 2:纯 CSS 图标(不用 JS)
- UnoCSS 还能做“纯 CSS 图标”:通过类名例如
i-ph-.../i-mdi-.../i-logos-...等生成图标。 - 配合 variants 可实现:hover 切换图标、dark 模式切换图标。
- 依托 Iconify,可使用大量图标集(作者描述为一百多个集合、上万图标级别),并按需使用。
- 实现同样宣称不超过 100 行,预设是
@unocss/preset-icons。
CSS 作用域:UnoCSS 对 “Preflight” 的态度与后果
- 作者在 Tailwind/Windi 里遇到的另一个问题是 Preflight(样式预检/重置):新项目里很香,但当你要与其他 UI 框架共存、或写共享组件时,重置容易引发冲突、破坏现有 UI。
- UnoCSS 的选择非常明确:不内置 Preflight,把 CSS reset 的控制权交给用户/上层框架(Normalize.css、Reset.css 或 UI 框架自带 reset)。
- 这也打开了更多“作用域”可能性:例如 Vite 插件的实验模式
scoped-vue,给每个组件生成 scoped 的原子 CSS,便于做组件库而不和用户 CSS 冲突。 - 还在探索:Web Components、MPA 的 CSS code-splitting、模块级作用域等。
性能:为什么 UnoCSS 能快到“几乎零开销”
作者说性能并非唯一重点,但 benchmark 结果“吓人”:在一次对比里,UnoCSS 相对 Tailwind JIT 快到约 200 倍数量级(当时版本对比)。
跳过解析/AST:用字符串拼接 + 缓存
- Tailwind 依赖 PostCSS AST;Windi 有自定义解析器/AST。
- UnoCSS 认为开发期工具类变化没那么频繁,因此用高效的字符串拼接直接产出 CSS,并对 class 与生成结果做缓存:再遇到相同 utility 时可绕过匹配与生成。
单次迭代:不做“额外文件扫描/监听”,直接吃构建工具现成的内容
Windi/Tailwind JIT 通常做预扫描 + 文件监听;文件 I/O 带来额外开销,而且构建工具本身也会读取这些文件,存在重复劳动。
UnoCSS 更偏向深度绑定 Vite:利用 Vite 的 transform(code, id) 钩子(Vite 会遍历每个模块并给你内容),插件只收集内容并 scan,不改代码;CSS 通过虚拟模块输出。
好处:
- HMR 时 Vite 会重新触发 transform,因此 UnoCSS 在“一次加载链路”里完成所有工作;没有重复文件 I/O、也不需要额外 watcher。
- 扫描依赖模块图而不是 glob:只有真正进入应用构建的模块会影响 CSS,而不是目录里所有文件。
unocss 状态
- 结论:能用,但有注意事项;当时仍处实验阶段,生成结果可靠,但 API 可能有破坏性改动(会遵循 semver,但仍要有心理准备)。
- 它不是为了替代 Windi/Tailwind,作者甚至建议:不要把现有项目完全迁移到 UnoCSS;更推荐在新项目试用,或作为现有框架的补充(比如只用 icons preset,或只用自定义规则)。
- 文章所在网站也构建在 UnoCSS 之上,作为参考。
操作性结论
- 如果你在意“原子化 CSS 的未来形态”,作者的方向是:把框架做薄,把能力拆成可组合的规则/变体/预设,让“按需”不只是生成策略,而是定制方式的核心。
- 如果你痛恨频繁查文档配配置、想要更“直觉可推导”的体验,UnoCSS 的 dynamic rules/variants 设计正是为此服务。
- 如果你做组件库/多框架共存,Preflight 的冲突是现实问题;UnoCSS “不内置 preflight、把 reset 交给你”是一个明确的立场。
- 如果你在 Vite 上追求极致 HMR 体验,UnoCSS 强调“吃 Vite 模块图、单次迭代、避免额外 I/O”这一套非常对路。
“这篇文章已经过去 5 年了”,4 年在前端工具链里已经是“好几代”了,而且现在已经是“成熟方案”而不是“激进方案”。
UnoCSS = 原子化 CSS 的“底层引擎”选项之一,现在它和 Tailwind 的关系已经从:“挑战者 / 实验品” 变成了 “不同哲学下的成熟工程解”
“可以放心上生产线用,但前提是你清楚自己在用哪一层能力。”
preset-unopreset-wind(Tailwind 风格)preset-iconspreset-attributify- shortcuts / rules / variants
- Vite / Nuxt / Vue / React 集成
这些已经是 主航道
总结:UnoCSS 早就不再是实验品,而是一个“稳定的、偏底层的原子化 CSS 引擎”,对于理解 Tailwind 的开发者来说,它甚至是更高级、更自由的生产级选择。