UnoCSS Compile Class 转换器实战:用:uno:标记将工具类批量编译为单一类名
【免费下载链接】unocssThe instant on-demand atomic CSS engine.项目地址: https://gitcode.com/GitHub_Trending/un/unocss
导读
@unocss/transformer-compile-class是 UnoCSS 官方提供的源码级转换器,它把一段由多个原子化工具类组成的 class 字符串,在构建期编译为一个带哈希后缀的单一类名,从而显著压缩 HTML 体积、简化 DOM 类名、并将完整样式规则统一收拢到 CSS 中。本文基于该包在仓库中的 README 与 源码实现 展开,你将掌握安装配置、:uno:触发标记的写法、全部可调选项(trigger / classPrefix / hashFn / keepUnknown / alwaysHash / layer),以及如何在团队中用 ESLint 规则强制推行这一编译模式。
一、它解决什么问题:把"一串类名"编译成"一个类名"
在使用原子化 CSS 时,一个元素往往要挂载五六个甚至十几个工具类:
<div class="text-sm font-bold hover:text-red"></div>这种写法虽然灵活,但会让 HTML 变得冗长。Compile Class 转换器借鉴了 Windi CSS 的 compilation mode(见仓库中 docs/transformers/compile-class.md 的说明),灵感来源于 issue #948。它允许你在 class 字符串开头加上:uno:标记,构建时由转换器把后续所有工具类"打包"成一个带哈希的短类名,并把对应的全部样式(含伪类、变体、响应式规则)合并进一条 CSS 规则。
该转换器由 src/index.ts 实现,返回一个标准的SourceCodeTransformer(源码第 84-156 行),通过enforce: 'pre'在 UnoCSS 生成 CSS 之前对源码字符串做改写。
二、安装与基础配置
1. 安装依赖
在项目中使用 pnpm / yarn / npm / bun 任一包管理器安装开发依赖:
pnpm add -D @unocss/transformer-compile-class # 或 yarn add -D @unocss/transformer-compile-class # 或 npm install -D @unocss/transformer-compile-class # 或 bun add -D @unocss/transformer-compile-class2. 在 uno.config.ts 中注册
// uno.config.ts import { defineConfig } from 'unocss' import transformerCompileClass from '@unocss/transformer-compile-class' export default defineConfig({ // ... transformers: [ transformerCompileClass(), ], })提示:该转换器已内置在
unocss聚合包中(见 unocss/src/index.ts 的export { default as transformerCompileClass }),因此你也可以直接从unocss导入,无需单独安装:import { transformerCompileClass } from 'unocss'
3. 与预设配合使用
转换器独立于各预设,可叠加在presetUno、presetWind3、presetWind4等任意预设之上。仓库的测试用例(test/transformer-compile-class.test.ts)正是以presetWind3()组合验证的。
三、核心用法:用:uno:标记要编译的类名
1. 基本写法
在 class 字符串的开头添加:uno:,其后紧跟要编译的工具类:
<div class=":uno: text-center sm:text-left"> <div class=":uno: text-sm font-bold hover:text-red" /> </div>转换器会在构建时将其编译为:
<div class="uno-qlmcrp"> <div class="uno-0qw2gr" /> </div>对应生成的 CSS 如下——注意伪类hover:、响应式变体sm:都一并被合并进编译类中:
.uno-qlmcrp { text-align: center; } .uno-0qw2gr { font-size: 0.875rem; line-height: 1.25rem; font-weight: 700; } .uno-0qw2gr:hover { --un-text-opacity: 1; color: rgb(248 113 113 / var(--un-text-opacity)); } @media (min-width: 640px) { .uno-qlmcrp { text-align: left; } }2. 工作机制:从匹配到替换
从 src/index.ts 可以梳理出转换器的核心处理链路:
- 匹配:用
trigger正则对源码做matchAll,找出所有形如:uno: ...的标记段(源码第 89 行); - 展开变体组:对匹配内容先执行
expandVariantGroup(展开hover:(...)这类变体组语法),再压缩连续空白为单个空格(源码第 96-98 行); - 区分已知/未知类:在
keepUnknown(默认开启)时,通过uno.parseToken逐个校验类名,已知类进入编译,未知类原样保留在字符串中(源码第 103-109 行); - 生成哈希类名:以编译内容为输入计算哈希,得到形如
uno-qlmcrp的类名(源码第 111-124 行); - 注册为快捷方式:把
[className, body]作为一条Shortcut写入uno.config.shortcuts,使 UnoCSS 按快捷方式机制生成样式(源码第 139-144 行); - 改写源码:用
MagicString的overwrite把原标记段替换为编译类名 + 保留的未知类(源码第 150 行)。
3. 哈希算法
默认哈希函数位于同一文件的第 159-169 行:基于 FNV-1a 变体,用0x811C9DC5作为初始值,对每个字符迭代异或与移位累加,最后取 36 进制后 6 位并补零。因此相同工具类集合在任何位置都会得到稳定的相同类名——这也是它能安全用于 HMR 与增量构建的前提。
4. 跨文件去重与失效
转换器内部维护compiledClass映射(源码第 76 行,对应 issue #2866 的修复),当某个编译类的内容发生变化时,会调用uno.invalidateToken与invalidate()触发重新生成;测试 test/transformer-compile-class.test.ts 专门验证了"内容变化时 CSS 恰好更新两次"这一行为。
四、选项详解:自定义触发符、前缀与哈希行为
转换器接收一个CompileClassOptions对象(完整类型定义见 src/index.ts),各选项含义如下:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
trigger | string \| RegExp | /(["'])\s*:uno-?(? \S+)?:\s([\s\S]*?)\1/g| 触发标记的匹配规则,默认匹配:uno:`;支持传入字符串或正则 | |
classPrefix | string | 'uno-' | 编译后类名的前缀 |
hashFn | (str: string) => string | 内置 FNV 哈希 | 自定义哈希函数,用于生成类名后缀 |
keepUnknown | boolean | true | 是否把 UnoCSS 无法识别的类名原样保留在字符串中 |
alwaysHash | boolean | false | 显式命名编译类时,是否仍然追加哈希后缀 |
layer | string | 无 | 生成规则所属的 CSS layer 名称 |
1. 自定义触发符(trigger)
默认触发符是:uno:,你可以改成任何关键词。从源码第 80-82 行可见其向后兼容逻辑:传字符串时会被转义后包装为正则,传正则字面量时直接使用。
字符串形式:
transformerCompileClass({ trigger: ':uno:', })正则形式(带命名捕获组)——这是最强大的用法。通过定义名为name的捕获组,可以给编译类指定自定义类名而非哈希:
export default defineConfig({ transformers: [ transformerCompileClass({ trigger: /(["'`]):uno(?:-)?(?<name>[^\s\1]+)?:\s([^\1]*?)\1/g, }), ], })该正则匹配:uno-MYNAME:,并用MYNAME与classPrefix拼接作为最终类名,例如生成.uno-MYNAME。注意:使用正则触发符时必须带上全局标志/g。
仓库测试覆盖了三种触发场景(test/transformer-compile-class.test.ts):
// 无自定义名::custom: 触发,哈希类名 <div class=":custom: bg-red-500 text-xl"> → <div class="uno-trmz0g"> // 带自定义名 + 自定义前缀::custom-foo: 触发,生成 something-foo <div class=":custom-foo: bg-red-500 text-xl"> → <div class="something-foo"> // 复杂自定义名::custom-foo_bar-baz: 触发 <div class=":custom-foo_bar-baz: bg-red-500 text-xl"> → <div class="uno-foo_bar-baz">显式命名与冲突检测:当使用显式类名时,若同一名字被用于不同工具类组合,转换器会直接抛错(源码第 126-130 行):
Duplicated compile class name "uno-foo". One is "w-2" and the other is "w-1". Please choose different class name or set 'alwaysHash' to 'true'.测试 test/transformer-compile-class.test.ts 对此有专门断言。解决办法有两种:换一个不冲突的名字,或设置alwaysHash: true强制追加哈希后缀区分。而哈希生成的类名天然不会冲突——测试第 123-135 行验证了w-1 h-1、w-2 h-2、h-1 w-1三个组合会得到三个不同的类名(即使元素顺序不同)。
2. 类名前缀(classPrefix)
编译类名的默认前缀是uno-。如果你希望类名更短、更不易与业务类名冲突,可以自定义:
transformerCompileClass({ classPrefix: 'u-', // 生成 u-qlmcrp 这类类名 })3. 保留未知类(keepUnknown)
默认keepUnknown: true时,转换器会用uno.parseToken逐词校验:能被 UnoCSS 解析的类进入编译,解析不了的(如普通 CSS 类名foo)则原样保留在元素的 class 字符串里。测试中的快照展示了这一点:
<div class=":uno: text-center sm:text-left foo"> <div class=":uno: text-sm font-bold hover:text-red"/> </div>编译后:
<div class="uno-qlmcrp foo"> <div class="uno-0qw2gr"/> </div>若设为false,未知类会被直接丢弃。
4. 自定义哈希函数(hashFn)
需要更短、更安全或更可控的类名时,可传入自定义哈希函数:
transformerCompileClass({ hashFn: (str) => someCustomHash(str).slice(0, 6), })5. 指定样式层(layer)
若项目启用了 UnoCSS 的 layer 分层机制,可通过layer把编译规则归入指定层(源码第 139 行在注册 shortcut 时透传 layer 配置):
transformerCompileClass({ layer: 'utilities', })6. 多行与复杂场景
trigger正则支持跨行匹配。仓库测试(test/transformer-compile-class.test.ts)验证了如下多行写法同样能正确编译:
<div class=" :uno: w-1 h-1 bg-red text-blue ">五、配套工具:用 ESLint 强制推行编译模式
为了让整个团队统一使用:uno:编译模式(避免某些人写、某些人不写导致类名风格割裂),仓库提供了配套的 ESLint 规则@unocss/enforce-class-compile(实现见 eslint-plugin/src/rules/enforce-class-compile.ts,并在 eslint-plugin/src/plugin.ts 中注册)。
在 ESLint 配置中开启:
{ "plugins": ["@unocss"], "rules": { "@unocss/enforce-class-compile": "warn" } }该规则会提示"未使用:uno:标记"的工具类字符串,帮助团队在编码阶段就落实编译约定。更多信息可参考官方文档 docs/integrations/eslint.md。
六、适用场景与使用建议
推荐使用编译模式的场景:
- 追求极致的 HTML 体积与更干净的 DOM 类名(如对外的落地页、营销页);
- 类名需要被第三方脚本、埋点或后端模板识别的场景;
- 希望把"变体 + 响应式 + 伪类"样式合并到单条 CSS 规则、减少样式重复的场景。
需要注意的限制:
- 转换器作用于源码文本,需要接入 UnoCSS 的构建链路(Vite / Nuxt / Webpack 等集成中配置
transformers),且enforce: 'pre'意味着它会在生成 CSS 前改写代码; - 未标记
:uno:的类名保持原样,不会被编译; - 显式命名(
:uno-name:)时要注意名字冲突,建议配合alwaysHash或保持命名唯一; - 若项目中存在动态拼接的 class 字符串,编译只对静态可解析的部分生效,动态部分建议交给 safelist 等机制处理。
七、相关资源
- 包源码与完整类型定义:transformer-compile-class/src/index.ts
- 官方文档:docs/transformers/compile-class.md
- 测试用例(含全部选项的行为快照):test/transformer-compile-class.test.ts
- 聚合包导出入口:packages-presets/unocss/src/index.ts
- 相关转换器系列文档:docs/transformers/directives.md、docs/transformers/variant-group.md
许可
本包遵循 MIT License,Copyright © 2021-PRESENT Anthony Fu。
【免费下载链接】unocssThe instant on-demand atomic CSS engine.项目地址: https://gitcode.com/GitHub_Trending/un/unocss
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考