Unocss为什么突然火了
前言
UnoCSS是即时原子CSS引擎,其设计具有灵活性和可扩展性。核心是不固执己见的,所有的CSS实用程序都是通过预设提供的。
首先,理解 Unocss 离不开原子化 CSS 的概念:
-
原子化 CSS: 将样式拆解到最细粒度的单一原子,例如
.m-4
代表margin: 4px;
,.text-red
代表color: red;
。开发者通过在 HTML 或 JSX 中组合这些原子类来构建复杂的 UI。 -
优势: 极高的可复用性、极少的样式冲突、优秀的可维护性、设计约束性强。
-
传统原子化框架的痛点: 如 Tailwind CSS,需要预先生成完整的 CSS 文件(包含所有可能的类),即使项目中只使用了其中一小部分。这可能导致:
- 开发环境 CSS 文件体积庞大,影响热更新速度。
- 生产环境需要配合 PurgeCSS 等工具进行 Tree Shaking,增加构建步骤和复杂度。
- 自定义主题或添加新规则需要配置复杂的
tailwind.config.js
文件。
Unocss 的核心突破就在于它解决了这些痛点!
Unocss 是什么?
简单来说,Unocss 是一个按需生成原子 CSS 的工具 。它不是一个预先生成所有类的庞大 CSS 文件库,而是一个 引擎:
- 即时扫描 (On-demand): Unocss 在开发过程中(通过 Vite、Webpack 等构建工具的插件或独立运行时)实时扫描你的源代码(HTML, JS, JSX, Vue, Svelte 等)。
- 识别实用类: 它识别出你在代码中实际使用的类名(如
m-4
,text-blue-500
,hover:bg-gray-100
)。 - 按需生成 CSS: 只为你实际用到的类 动态生成对应的 CSS 规则。
- 注入样式: 将这些生成的、最小化的 CSS 注入到你的页面中。
核心特性与优势
-
极致的性能与体积:
- 开发环境: 热更新(HMR)速度极快,因为只重新生成变化的少量 CSS,而不是操作整个庞大的 CSS 文件。
- 生产环境: 生成的 CSS 天然就是最小化的,仅包含项目中实际使用的样式规则。无需额外的 PurgeCSS 步骤,构建输出精简到极致。
-
高度可定制与可扩展:
-
预设系统: Unocss 本身非常核心且轻量,其强大功能通过预设 (
presets
) 实现。官方提供了丰富的预设:@unocss/preset-uno
:默认预设,提供最通用的实用程序(类似 Tailwind 的核心功能)。@unocss/preset-wind
:提供与 Tailwind CSS v2/v3 高度兼容的类名和规则。@unocss/preset-attributify
:革命性的特性,允许以 HTML 属性的方式使用原子类(如<div text="blue-500 hover:red" m-4>
),极大提升可读性和组织性。@unocss/preset-icons
:轻松按需使用图标(如 Iconify)。@unocss/preset-typography
:提供排版样式。@unocss/preset-web-fonts
:轻松引入网络字体。@unocss/preset-tagify
:将特定标签自动转换为原子类。
-
自定义规则: 你可以轻松定义自己的原子类规则。无论是简写、别名还是全新的实用程序,都可以通过简洁的配置实现。
-
可变修饰: 规则支持动态值(如
m-${size}
),并在扫描时匹配和生成对应的 CSS。
-
-
智能与灵活:
- CSS 指令: 支持在类名中内联编写任意 CSS (
[attr:value]
),提供突破原子类限制的灵活性。 - Shortcuts: 允许你将常用的多个原子类组合定义成一个简短的别名,提高代码可读性和编写效率。
- Layer 排序: 控制生成 CSS 的顺序,解决样式优先级问题。
- CSS 指令: 支持在类名中内联编写任意 CSS (
-
框架无关: 虽然与 Vite 集成体验最佳,但也支持 Webpack、Nuxt、Vue CLI、SvelteKit、Astro 等主流前端工具和框架。
-
熟悉而现代: 如果你熟悉 Tailwind CSS,那么使用 Unocss 的
preset-wind
会感觉非常亲切,同时又能享受到 Unocss 带来的性能和灵活性提升。
Unocss 是如何工作的?
-
集成: 在你的项目中安装 Unocss 核心包和所需的预设,并在构建工具配置(如
vite.config.js
)中引入 Unocss 插件。 -
配置: 创建一个
unocss.config.ts
文件,选择需要的预设和进行自定义规则、主题、Shortcuts 等配置。 -
开发/构建:
- 当你启动开发服务器 (
vite dev
) 或进行构建 (vite build
) 时: - Unocss 插件会监视你的源代码文件。
- 对文件内容进行扫描,提取出所有匹配预设和自定义规则的类名、属性名等。
- 根据匹配到的内容,动态生成对应的 CSS 规则。
- 将这些规则注入到页面的
<style>
标签中(开发环境)或打包到最终的 CSS 文件中(生产环境)。
- 当你启动开发服务器 (
-
使用: 在组件或 HTML 中,像使用普通 CSS 类或属性(如果用了 attributify)一样使用定义好的原子类。
按需生成
Unocss 实现「监视源代码文件并动态插入 CSS」的核心机制主要依赖于构建工具插件(如 Vite、Webpack 等)和其内部的 「按需扫描」引擎。下面详细解释其工作流程和技术原理:
一、核心流程(以 Vite 为例)
-
集成插件:
- 在
vite.config.js
中引入unocss/vite
插件。 - 插件初始化时会读取你的
unocss.config.ts
配置(预设、规则、主题等)。
- 在
-
启动开发服务器 (
vite dev
) :- Vite 启动开发服务器,并加载 Unocss 插件。
- Unocss 插件会向 Vite 注册关键的 「构建钩子 (Hooks)」。
-
文件监听与扫描:
-
监听文件变化 :
Vite 本身会监视项目文件(
*.html
,*.js
,*.jsx
,*.vue
,*.svelte
等)。当任何文件被修改、添加或删除时,Vite 会触发相应的 「热更新 (HMR)」 流程。 -
拦截文件内容 :
Unocss 插件通过注册 Vite 的
transform
钩子 ,拦截 所有源代码文件的处理过程。每当一个文件被 Vite 加载或更新时,Unocss 都能获取到它的 内容 (content
) 和 文件路径 (id
) 。 -
执行扫描 :
Unocss 的核心引擎
扫描 (scan)
这些文件的内容:-
解析 HTML 标签、属性、类名 (
class
/className
)、Vue/Svelte 模板语法等。 -
识别其中符合规则的字符串:
- 预设定义的原子类名 (如
m-4
,text-blue-500
,hover:bg-gray-100
) - Attributify 模式下的属性名 (如
text="blue-500"
,hover:bg="gray-100"
) - Icons 预设的图标类名 (如
i-mdi:home
) - 自定义规则匹配的字符串
- 内联 CSS 指令 (如
[color:red]
)
- 预设定义的原子类名 (如
-
将这些识别到的 「有效 token」 收集起来。
-
-
-
按需生成 CSS:
-
Unocss 引擎根据收集到的 所有有效 token ,结合配置中的规则 (rules) 、主题 (theme) 和 Shortcuts ,动态计算 出对应的 最小化 CSS 规则。
-
这个过程是 增量式 的:
- 初次启动:扫描所有入口文件及其依赖,生成完整 CSS。
- 文件更新:只重新扫描变化的文件 ,计算 新增的 token 和 失效的 token ,增量更新 CSS 规则集。这是 HMR 极快的关键!
-
-
注入 CSS:
-
开发环境:
-
Unocss 插件在 Vite 的
configureServer
钩子中,向开发服务器注入一个 中间件 (Middleware) 。 -
这个中间件会拦截一个特定的虚拟请求 (例如
/@unocss.css
)。 -
当浏览器请求这个虚拟 CSS 文件时,中间件会实时返回当前内存中生成的完整 CSS 文本。
-
在项目的入口 HTML 文件 或主 JS 文件 中,Unocss 插件会自动添加一个
<link>
标签引入这个虚拟 CSS 文件:html<!-- 自动注入到 HTML 中 --> <link rel="stylesheet" href="/@unocss.css">
-
HMR 更新: 当 CSS 规则变化时,Unocss 通过 Vite 的 HMR API 通知浏览器动态更新 这个虚拟样式表,实现无刷新样式更新。
-
-
生产环境 (
vite build
):- Unocss 插件注册 Vite 的
buildEnd
或generateBundle
钩子。 - 在构建的最后阶段,扫描所有最终打包产物中的文件。
- 将扫描到的所有 token 生成最终的、最小化的 CSS 字符串。
- 将这个 CSS 字符串作为一个虚拟 CSS 模块 ,通过 Vite 的打包流程输出到最终的 CSS 文件 (如
assets/index.xxxx.css
),并自动被入口 HTML 引用。
- Unocss 插件注册 Vite 的
-
关键技术点解析
-
「按需」的本质:
- 不是预生成所有可能类: 不像 Tailwind 需要先生成包含所有可能类的巨大 CSS 文件。
- 运行时扫描 + 动态生成: 只生成代码中实际出现 的类对应的 CSS 规则。这是其体积极致小 和开发 HMR 极快的根本原因。
-
与构建工具深度集成:
- 利用构建工具的模块图 (Module Graph): Unocss 插件能获取 Vite 处理的所有模块及其依赖关系 。这确保了即使类名是动态生成的(如
class="text-${color}-500"
),只要color
的可能值在代码中被静态引用过,Unocss 也能正确扫描到并生成对应的 CSS (如text-red-500
,text-blue-500
)。 - 利用构建工具的 HMR API: 实现 CSS 的即时、精准更新。
- 利用构建工具的模块图 (Module Graph): Unocss 插件能获取 Vite 处理的所有模块及其依赖关系 。这确保了即使类名是动态生成的(如
-
虚拟模块 (Virtual Modules):
/@unocss.css
是一个典型的虚拟模块 。它不存在于物理磁盘上,而是由 Unocss 插件在内存中动态生成内容并提供给 Vite 服务器或构建流程。- 构建工具将虚拟模块当作普通文件处理,简化了集成。
-
高效的扫描引擎:
- Unocss 的核心引擎 (
@unocss/core
) 实现了高效的字符串匹配算法 和 CSS 规则生成器。 - 它根据配置的规则(静态规则、正则表达式规则、动态规则)快速匹配文件内容中的 token。
- 增量扫描确保只处理变更部分,性能开销极小。
- Unocss 的核心引擎 (
-
支持多种语法:
- 引擎不仅能扫描
class
属性,还能处理 Vue/Svelte 的模板语法、JSX 中的className
、Attributify 模式的属性、甚至纯 JS 字符串(通过特定注释或约定)。 - 插件内部包含针对不同文件类型(
.vue
,.jsx
,.svelte
等)的解析器。
- 引擎不仅能扫描