airi 项目 UnoCSS 实战:用 transformerCompileClass 将多类名编译为单一哈希类,压缩 HTML 体积
导读
本文聚焦 UnoCSS 内置转换器 transformerCompileClass:它能在构建期把元素上一长串工具类(utility class)合并为单一哈希类名(如 uno-qlmcrp),从而显著缩短 HTML 体积、隐藏类名语义并减少浏览器解析开销。文章以 transformers/compile-class 参考文档 为骨架,结合 airi 仓库中实际生效的 uno.config.ts(以及其各子应用的 vite.config.ts 等配置)给出可落地的最佳实践,读者学完后可以立即在自己的 airi 子应用或独立 UnoCSS 项目中启用并定制该转换器。
一、什么是 transformerCompileClass
在 airi 这类大型 monorepo 前端(apps/component-calling、apps/stage-web、apps/stage-pocket、packages/stage-ui、docs 等大量依赖 UnoCSS 的场景)中,模板里常常会写满重复的工具类字符串,例如每个卡片都要重复 "flex items-center justify-center gap-2 text-sm"。这些字符串既占 HTML 体积,也会让每次浏览器解析 class 属性付出额外成本。
transformerCompileClass 解决的问题正是"类名压缩":它以触发前缀(默认 :uno:)标记需要合并的 class 列表,然后在编译阶段把这一组工具类替换成一个根据类名内容哈希生成的短类名,最终为这个哈希类生成对应的完整 CSS。由于它只作用于 HTML / 模板文本本身(而不是扫描整个项目的提取管线),你可以在不影响其他工具类的前提下,按需把高频复用的类组合"编译"掉。
本技能文档来自 SKILL.md 中 UnoCSS 的官方 transformers 章节,源码层面的行为以仓库中 unocss(catalog 版本,声明于 package.json)为准。
二、安装与基础配置
transformerCompileClass 随主包 unocss 一起导出,因此只要项目中已经安装了 unocss(airi 根 package.json 及 packages/stage-ui/package.json 均以 catalog: 形式依赖),就无需额外安装。
在 uno.config.ts 中通过 defineConfig 的 transformers 数组开启:
import { defineConfig, transformerCompileClass } from 'unocss'
export default defineConfig({
transformers: [
transformerCompileClass(),
],
})
与 airi 的实际配置对比:仓库根 uno.config.ts 目前启用了 transformerDirectives(配置了 --at-apply 变量)与 transformerVariantGroup 两个转换器。若在某个对包体积敏感的页面/组件里需要启用类名压缩,将上面的 transformerCompileClass() 追加进同一数组末尾即可,二者互不冲突(叠加规则见下文"与其他 Transformer 组合")。
三、核心用法:用 :uno: 前缀标记可编译类
在模板中,为希望被合并进单一哈希类的 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>
要点说明:
:uno:前缀只出现在源码中的标记位置,最终产物中不会存在该字符;- 编译基于这一组工具类字符串的完整内容做确定性哈希,因此完全相同的类组合会得到完全相同的哈希类名,天然支持复用与缓存;
sm:text-left、hover:text-red这类带变体的工具类同样被正确归并进哈希规则(响应式规则会被抽到对应的@media块中,见下文生成 CSS);- 未加前缀的普通 class 不受影响,可作为"旁路"继续走常规提取管线。
编译产物对应的 CSS
对上述示例,转换器生成的样式大致如下:
.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;
}
}
可以看到三点实现细节:
- 原本 HTML 里的
"text-center sm:text-left"两个 token,被收拢为一条媒体外规则 + 一条@media (min-width: 640px)规则,浏览器只需针对一个类名去匹配; - hover 变体被展开为
.uno-0qw2gr:hover独立规则,说明转换器内部仍然复用了 UnoCSS 的变体(variants)展开机制,与 core-variants 参考文档 描述一致; - CSS 变量(
--un-text-opacity)等 wind 系预设产物保持原样,因此该转换器与 airi 使用中的presetWind3等预设完全兼容。
四、全部可配置项(Options)
transformerCompileClass 接受一个对象参数:
transformerCompileClass({
// 自定义触发字符串(默认:':uno:')
trigger: ':uno:',
// 自定义生成的类名前缀(默认:'uno-')
classPrefix: 'uno-',
// 自定义类名哈希函数,输入为待编译的类名字符串
hashFn: (str) => /* custom hash */,
// 是否在编译后的类名旁保留原始类(默认:false)
keepOriginal: false,
})
逐项实战解读:
trigger:默认':uno:'。若与项目中其它标记(例如 attributify 或自定义内容管道)冲突,可改成更独特的字符串,如':u:'。该前缀仅作为源码中的"编译标记",不会输出到最终 CSS/HTML;classPrefix:默认'uno-'。用于区分产物中的哈希类与手写类,便于在调试面板里一眼识别,也可避免与第三方样式表前缀冲突;hashFn:默认是内置哈希(fNV-1a 变体并做短 base36 编码,这里不必依赖具体算法,保证同输入同输出即可)。传入自定义函数时必须保持确定性(纯函数),否则同一段源码在不同构建间会产生不同类名,破坏产物缓存与离线渲染的一致性;若与 SSR/预渲染(airi 的apps/stage-web、docs均有静态站点/水合场景)配合,还需保证函数在 Node 与浏览器两端结果一致;keepOriginal:默认false。调试期可临时置为true,让源码类名仍以普通类形式保留在元素上(用于人工核对样式),线上构建再关闭以最大化压缩收益。
五、典型使用场景
原文档列出的核心收益在 airi 的规模化组件场景中对应关系如下:
- 更小的 HTML(Smaller HTML):聊天气泡、按钮组、设置页条目这类高频组件往往带 5~10 个工具类,模板循环渲染时重复字符串会成倍放大体积。将固定样式块用
:uno:编译为哈希类后,每条记录只输出uno-xxxxxx一个 token; - 混淆(Obfuscation):生产环境 HTML 中不再暴露
text-center、bg-primary-500这类与设计语义直接对应的类名,降低模板样式信息被直接扒走的可能。注意这是"减少可读信息"层面的收益,并非安全机制; - 性能(Performance):浏览器需要解析的 class 属性 token 数减少;同时由于相同组合只保留一份哈希类定义,CSS 中规则条数与选择器匹配成本相应下降。对需要实时语音、Live2D/Three 渲染等高交互场景(如
packages/stage-ui、apps/stage-tamagotchi的渲染层)可减少 DOM 解析干扰。需要强调的是:文档与仓库均未给出定量基准数据,该收益是结构性的定性优化,不应过度宣传为可测量的性能指标。
六、配套 ESLint 规则:enforce-class-compile
要在一个多人协作、多应用共存的仓库里推行"工具类必须带 :uno: 前缀"的约定,依赖人肉自觉并不现实。@unocss 生态提供了 @unocss/enforce-class-compile 规则(需在项目中引入配套的 ESLint 配置/插件后生效):
{
"rules": {
"@unocss/enforce-class-compile": "warn"
}
}
规则行为:
- 当
class属性不以:uno:开头时给出警告; - 支持自动修复(Auto-fix):为漏标前缀的 class 自动补上
:uno:。
也可传参控制细节:
{
"rules": {
"@unocss/enforce-class-compile": ["warn", {
"prefix": ":uno:",
"enableFix": true
}]
}
}
参数说明:
prefix:规则检测/补全时使用的前缀字符串,必须与uno.config.ts中transformerCompileClass({ trigger })的取值保持一致,否则规则会与转换器"打架";enableFix:是否允许eslint --fix自动加前缀,CI 中可以false+ 仅警告,先把存量代码放行,再渐进式迁移。
七、与其他 Transformer 的组合顺序
airi 根 uno.config.ts 已经示范了"多个 transformer 共存"的写法。官方推荐按如下顺序排列:
export default defineConfig({
transformers: [
transformerVariantGroup(), // 先处理变体组
transformerDirectives(), // 再处理指令
transformerCompileClass(), // 最后执行类名编译
],
})
顺序逻辑是:
transformerVariantGroup先把hover:(text-red underline)、sm:(...)这类括号组展开成普通工具类(该技能参考见 transformer-variant-group 文档),否则compileClass会拿到一个它无法解析的"伪类";transformerDirectives再把@apply、theme()等 CSS 指令就地解析(参考 transformer-directives 文档),这些属于 CSS 层处理,与 class 编译互不干扰;transformerCompileClass在最后做纯 class 层面的收敛,这样它处理的对象已经是展开、扁平化后的最终工具类集合,生成的哈希最稳定、漏项最少。
若在 airi 某个子应用(例如新增一个对模板体积敏感的页面应用)启用,直接在 apps/component-calling/uno.config.ts 这类子配置的 transformers 数组末尾 push 进 transformerCompileClass() 即可,无需改动根配置。
八、注意事项与仓库内的落地建议
结合 airi 的既有工程实践,补充几条使用边界:
- 该转换器是编译期、静态的:它只会替换"写死在模板里、带
:uno:前缀"的 class。若类名来自运行时拼接(例如:class="cond ? 'text-red' : 'text-gray'"),前缀不会命中,自然也不会被编译,仍走常规提取(unocss 会扫描模板源码中的字符串字面量,参考 core-extracting 文档); - 动态渲染的受控性:airi 中部分 UI 库组件(如 shadcn-vue 系封装)通过 props 拼接类名,根配置 uno.config.ts 为此把
components|src、stage-ui、ui目录纳入提取 include。要启用 compile-class 的组件必须保证其 class 是静态模板字符串,否则keepOriginal语义与运行时类都容易引起困惑; - 与 Safelist / 动态图标的关系:
transformerCompileClass只做 HTML 文本改写,不参与 safelist 生成;airi 根配置里通过safelist预生成的主题色/图标类(见 uno.config.ts)不会受影响; - 多应用一致性:monorepo 中若
docs、apps/*、packages/*各自维护 uno 配置,请让trigger、classPrefix、ESLintprefix三者在同一应用内保持一致,避免"规则要求前缀、转换器不识别前缀"的错位。
综上,transformerCompileClass 是 UnoCSS 中"小改动、大收益"的经典转换器:一行配置加入 transformers,在 HTML 模板上用 :uno: 前缀标记热点类组合,即可系统性压缩 HTML 体积、屏蔽类名细节并降低解析开销。结合 airi 仓库根 uno.config.ts 中已有的 transformerVariantGroup / transformerDirectives 配置,它完全可以无缝追加进同一管线,是面向生产环境静态产物优化值得优先尝试的一步。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00