UnoCSS Compile Class 转换器实战:用 `:uno:` 标记将工具类批量编译为单一类名
2026/9/14 0:00:03 网站建设 项目流程

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-class

2. 在 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. 与预设配合使用

转换器独立于各预设,可叠加在presetUnopresetWind3presetWind4等任意预设之上。仓库的测试用例(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 可以梳理出转换器的核心处理链路:

  1. 匹配:用trigger正则对源码做matchAll,找出所有形如:uno: ...的标记段(源码第 89 行);
  2. 展开变体组:对匹配内容先执行expandVariantGroup(展开hover:(...)这类变体组语法),再压缩连续空白为单个空格(源码第 96-98 行);
  3. 区分已知/未知类:在keepUnknown(默认开启)时,通过uno.parseToken逐个校验类名,已知类进入编译,未知类原样保留在字符串中(源码第 103-109 行);
  4. 生成哈希类名:以编译内容为输入计算哈希,得到形如uno-qlmcrp的类名(源码第 111-124 行);
  5. 注册为快捷方式:把[className, body]作为一条Shortcut写入uno.config.shortcuts,使 UnoCSS 按快捷方式机制生成样式(源码第 139-144 行);
  6. 改写源码:用MagicStringoverwrite把原标记段替换为编译类名 + 保留的未知类(源码第 150 行)。

3. 哈希算法

默认哈希函数位于同一文件的第 159-169 行:基于 FNV-1a 变体,用0x811C9DC5作为初始值,对每个字符迭代异或与移位累加,最后取 36 进制后 6 位并补零。因此相同工具类集合在任何位置都会得到稳定的相同类名——这也是它能安全用于 HMR 与增量构建的前提。

4. 跨文件去重与失效

转换器内部维护compiledClass映射(源码第 76 行,对应 issue #2866 的修复),当某个编译类的内容发生变化时,会调用uno.invalidateTokeninvalidate()触发重新生成;测试 test/transformer-compile-class.test.ts 专门验证了"内容变化时 CSS 恰好更新两次"这一行为。

四、选项详解:自定义触发符、前缀与哈希行为

转换器接收一个CompileClassOptions对象(完整类型定义见 src/index.ts),各选项含义如下:

选项类型默认值说明
triggerstring \| RegExp/(["'])\s*:uno-?(? \S+)?:\s([\s\S]*?)\1/g| 触发标记的匹配规则,默认匹配:uno:`;支持传入字符串或正则
classPrefixstring'uno-'编译后类名的前缀
hashFn(str: string) => string内置 FNV 哈希自定义哈希函数,用于生成类名后缀
keepUnknownbooleantrue是否把 UnoCSS 无法识别的类名原样保留在字符串中
alwaysHashbooleanfalse显式命名编译类时,是否仍然追加哈希后缀
layerstring生成规则所属的 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:,并用MYNAMEclassPrefix拼接作为最终类名,例如生成.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-1w-2 h-2h-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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询