UnoCSS Rollup / Rolldown 插件详解:在 Vite 之外按需生成 CSS 资源
【免费下载链接】unocssThe instant on-demand atomic CSS engine.项目地址: https://gitcode.com/GitHub_Trending/un/unocss
本文围绕 UnoCSS 的 Rollup 集成文档 展开,讲解如何在没有 Vite 的环境下,通过@unocss/rollup插件让 Rollup 或 Rolldown 构建管线接入 UnoCSS:包括插件安装与接入方式、虚拟入口uno.css的引入时机、uno.config.ts与内联配置两种配置方式,并结合仓库中插件的源码实现,深入剖析其内容提取、虚拟模块解析与 CSS 资源产出(emit asset)的完整工作流。读完本文,你可以独立搭建基于 Rollup/Rolldown 的 UnoCSS 构建链路,并理解插件在每个构建生命周期钩子中实际做了哪些事。
安装
根据文档,安装 UnoCSS 与打包器本身(二者均为 dev 依赖):
# pnpm pnpm add -D unocss rollup# yarn yarn add -D unocss rollup# npm npm install -D unocss rollup# bun bun add -D unocss rollup如果你使用的是 Rolldown,将命令中的rollup替换为rolldown即可。
从插件包的 package.json 可以确认其适用前提:rollup(^4.0.0)与rolldown(^1.0.0)都声明为可选的 peerDependencies,即插件同时兼容两者,但你的项目中至少需要安装其中一个;插件实际发布的包名为@unocss/rollup,而文档中使用的unocss/rollup入口是由元包unocss的 exports 字段./rollup转发到@unocss/rollup构建产物(见 packages-presets/unocss/package.json)。
在 Rollup 中使用
在rollup.config.ts中通过unocss/rollup导入插件并注册:
import UnoCSS from 'unocss/rollup' export default { input: 'src/main.ts', plugins: [ UnoCSS(), ], }在 Rolldown 中使用
Rolldown 的接入方式完全一致,只是入口改为unocss/rolldown:
import UnoCSS from 'unocss/rolldown' export default { input: 'src/main.ts', plugins: [ UnoCSS(), ], }入口模块引入uno.css触发 CSS 产出
在入口模块中显式导入虚拟 CSS 模块:
import 'uno.css'插件在打包阶段会据此将生成的 CSS 作为输出资源(asset)发出,产物文件名为uno.css,你需要在部署或 HTML 构建流程中自行将该资源引入应用(例如在 HTML 中<link>引入)。这与 Vite 集成“运行时自动改写 CSS 导入”的行为不同,是文档中强调的global模式语义:插件不劫持你的 CSS 管线,只负责把全量生成的 CSS 一次性 emit 出来。
从源码 packages-integrations/rollup/src/index.ts 可以确认这一行为:generateBundle钩子中,只有当构建过程中确实出现过 UnoCSS 虚拟入口的导入(内部vfsLayers非空)时,才会调用this.emitFile({ type: 'asset', name: 'uno.css', source: await generateCss() })产出资源;generateCss(index.ts#L30-L39)会先flushTasks等待所有提取任务完成,再用ctx.uno.generate(tokens, { minify: true })生成压缩后的 CSS,并按LAYER_IMPORTS顺序拼接各图层。
配置方式
独立配置文件
创建uno.config.ts:
import { defineConfig } from 'unocss' export default defineConfig({ // ...UnoCSS options })配置文件中的 options 遵循 UnoCSS 标准的UserConfig结构(presets、rules、variants、theme、content 等),与 Vite 等集成共用同一套核心配置类型。
直接向插件传入配置
也可以不落地配置文件,把配置对象作为参数传给插件:
import UnoCSS from 'unocss/rollup' UnoCSS({ // ...UnoCSS options })这一点与实现一致:插件工厂函数签名为RollupPlugin(configOrPath?: RollupPluginConfig<Theme> | string, defaults?: UserConfigDefaults)(index.ts#L16-L23),第一个参数既可以是配置对象,也可以是自定义配置文件路径;配置加载由共享层createContext中的createRecoveryConfigLoader(@unocss/config)完成,未显式传参时会自动从process.cwd()向上查找默认的uno.config.ts。
此外,插件还通过process.env.NODE_ENV推断环境(index.ts#L21):development时以dev模式运行,否则以build模式运行,影响生成器的输出策略(如 source map 相关行为)。
checkImport选项:忘记写import 'uno.css'时的告警
插件配置类型 types.ts 在标准UserConfig之外额外定义了一个 Rollup 集成特有的选项:
export interface RollupPluginConfig<Theme extends object = object> extends UserConfig<Theme> { /** * Warn when no UnoCSS virtual CSS entry is imported. * @default false */ checkImport?: boolean }默认值为false。启用后(UnoCSS({ checkImport: true })),如果整个构建过程中没有任何模块导入过 UnoCSS 虚拟入口,generateBundle会主动输出警告[unocss] Entry module not found. Did you add 'import uno.css' in your main entry?(index.ts#L99-L104)。由于该模式下 CSS 完全靠 emit 产出,漏写入口导入意味着产物中静默缺失全部 UnoCSS 样式,这个选项正是针对这一故障模式的安全网。
源码级原理:插件在各生命周期钩子中的工作流
Rollup 插件本体只有约 110 行(packages-integrations/rollup/src/index.ts),其完整调用链如下,可作为理解 UnoCSS 各类集成共性的最小范本:
1.buildStart:加载配置并启动全局内容提取
buildStart 钩子 先await ctx.ready等待配置加载完成,随后清空vfsLayers、tasks等状态并压入setupContentExtractor(ctx)任务。该函数定义在 virtual-shared/integration/src/content.ts,它会处理两类配置化内容源:
content.inline:内联文本/函数,直接extract其中出现的原子类;content.filesystem:使用tinyglobby按 glob 匹配文件(cwd为项目根),逐文件读取、过 filter、应用 transformers 后提取 tokens,读取按BATCH_SIZE = 50并发分批执行(content.ts#L62-L65)。
值得注意的是,Rollup 插件调用setupContentExtractor时不传shouldWatch(默认为false),即不做文件监听——提取在每次构建时一次性完成,这与 Rollup 一次性构建、无 dev server 的场景相符。
2.transform:逐模块提取与 transformer 应用
每个通过filter的代码模块都会走 transform 钩子:依次应用pre→ 默认 →post三类 transformers(如transformer-directives、transformer-variant-group),将转换后的代码交给extract(code, id)提取 tokens。filter的判定逻辑在 context.ts#L94-L98:含@unocss-ignore注释的模块跳过,含@unocss-include、@unocss-placeholder的模块强制纳入,其余走defaultPipelineInclude/Exclude的 glob 过滤(可用content.pipeline配置覆盖,content.pipeline: false可完全关闭文件提取)。
3.resolveId/load:把uno.css映射为虚拟模块
这是“global模式下必须显式import 'uno.css'”背后的机制。resolveId 钩子 将导入 id 交给共享层 resolveId,其匹配规则来自 constants.ts 中的 VIRTUAL_ENTRY_ALIAS:
export const VIRTUAL_ENTRY_ALIAS = [ /^(?:virtual:)?uno(?::(.+))?\.css(\?.*)?$/, ]可以确认以下导入形式都会被解析为 UnoCSS 虚拟入口:
import 'uno.css'—— 全部图层;import 'virtual:uno.css'—— 带virtual:前缀的等价写法;import 'uno:<layer>.css'—— 只产出指定图层(layer)的 CSS。
匹配成功后,插件把 id 重写到形如/__uno.css的路径(前缀由virtualModulePrefix决定,默认__uno,见 context.ts#L116-L132),再在 Rollup 侧注册一个带\0前缀的纯虚拟模块 id(\0unocss:<layer>),同一 layer 被多个文件重复导入时只会保留首次出现并给出warn。load钩子对该虚拟模块返回code: ''(index.ts#L84-L94),因为 CSS 内容不走模块图,而是最终统一走 emit。
4.generateBundle:一次性 emituno.css
如前文所述,generateBundle在vfsLayers非空时调用generateCss()并 emit 名为uno.css的 asset。由于提取与生成都发生在此阶段,最终产出的 CSS 覆盖了所有被transform捕获的模块 +content配置声明的内容源中出现过的全部 tokens,这正是global模式的含义:不依赖某个具体 CSS 模块在模块图中的位置,而是全局收集、集中产出。
适用前提与限制小结
- 依赖前提:
rollup ^4.0.0或rolldown ^1.0.0二者至少装其一(packages-integrations/rollup/package.json 中均为 optional peer); - 必须显式
import 'uno.css'(或uno:<layer>.css),否则不会产出任何 CSS,可用checkImport: true获得构建期告警; - 该集成只做一次性 CSS 产出(minified,无 watch),适合库构建、CLI 打包等离线场景;需要 HMR、按模块注入 CSS 的场景应改用
unocss/vite等集成; - 插件导出的
defineConfig只是原样返回配置对象(index.ts#L12-L14),类型约束来自RollupPluginConfig(即UserConfig+checkImport)。
【免费下载链接】unocssThe instant on-demand atomic CSS engine.项目地址: https://gitcode.com/GitHub_Trending/un/unocss
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考