- 前端
- 开发工具
【免费下载链接】lightningcss
An extremely fast CSS parser, transformer, bundler, and minifier written in Rust.
CSS 默认是全局命名空间:不同文件里同名类、id、自定义属性或@keyframes会互相覆盖,这在大型项目中极易引发样式冲突。Lightning CSS 原生支持 CSS modules 为骨架,结合 src/css_modules.rs 等源码深入讲解启用方式、exports数据结构、composes组合、:global例外、局部 CSS 变量、自定义命名 pattern、pure 模式以及 feature scoping 关闭等全部能力。
为什么需要 CSS modules:全局标识符的冲突问题
在默认情况下,CSS 中的所有标识符都是全局的。如果两个文件定义了相同的类名、id、自定义属性或@keyframes动画名,它们就会互相冲突、互相覆盖,最终样式取决于加载顺序。CSS modules 的解决思路是:把每个文件中定义的类名与标识符视为唯一——每个类名或标识符都会被重命名,加入一个唯一的 hash;同时导出一份映射表给 JavaScript,以便在模板或脚本中引用这些编译后的类名。
在 Lightning CSS 的实现中,这一机制由 src/css_modules.rs 支撑:文件顶部的模块注释明确写道——CSS modules 是“局部作用域 CSS 文件中名字的一种方式”,涵盖类名、id、keyframe 动画名以及任何使用CustomIdent类型的位置。启用 CSS modules 后,打印样式表时会为声明的名字追加 hash,并同步更新对这些名字的引用,最终返回原名到编译名的映射。
启用 CSS modules:API 选项与 CLI 标志
启用方式有两种:使用 JS API 时传入cssModules选项,使用 CLI 时加--css-modules标志。
import {transform} from 'lightningcss'; let {code, map, exports} = transform({ // ... cssModules: true, code: Buffer.from(` .logo { background: skyblue; } `), });在 TypeScript 定义 node/index.d.ts 中,cssModules的类型是boolean | CSSModulesConfig——既可以传true直接启用,也可以传一个配置对象来细化行为(见下文各节)。cssModules为true时,内部会使用 src/css_modules.rs 中Config::default(),其默认值为:
pattern:[hash]_[local]dashed_idents:falseanimation、grid、container、custom_idents:truepure:false
CLI 侧的处理在 src/main.rs:当传入--css-modules时,会读取--css-modules-pattern解析 pattern,读取--css-modules-dashed-idents开启局部变量,其余字段走默认值。
理解exports对象:原名字到编译名的映射
调用transform后,除了编译后的代码和 source map,还会额外返回一个exports对象。exports中的每个属性把源码 CSS 中的原始名字映射到编译后(已 hash 的)名字。你可以在 JavaScript 或模板文件中利用这份映射来引用编译后的类名与标识符。
针对上面示例,exports对象大致如下:
{ logo: { name: '8h19c6_logo', isReferenced: false, composes: [] } }这个结构对应 Rust 侧的 CssModuleExport:
name:编译后的局部名字;composes:该导出组合(compose)的其他名字列表;is_referenced:该导出在当前文件中是否被引用。
CssModuleExport在开启serde/nodejsfeature 时会序列化为 camelCase(见 src/css_modules.rs 的serde(rename_all = "camelCase")),因此 JS 侧字段就是name、isReferenced、composes。TypeScript 侧的类型定义在 node/index.d.ts。
值得注意的细节:默认 pattern[hash]_[local]下,hash 由相对项目根目录的文件路径计算得出(见 src/css_modules.rs 的注释“Make paths relative to project root so hashes are stable”),这样 hash 在不同机器上保持一致,便于缓存与长期复用。
类组合(composes):实现样式混入
CSS modules 中的样式规则可以通过composes属性引用其他类,被引用的类会在组合类被使用的同时一并生效,这实际上提供了一种“样式混入”(style mixins)机制。
.bg-indigo { background: indigo; } .indigo-white { composes: bg-indigo; color: white; }上例中,只要indigo-white类被应用,bg-indigo类也会一并应用。这在 Lightning CSS 返回的exports对象中体现如下:
{ 'bg-indigo': { name: '8h19c6_bg-indigo', isReferenced: true, composes: [] }, 'indigo-white': { name: '8h19c6_indigo-white', isReferenced: false, composes: [{ type: 'local', name: '8h19c6_bg-indigo' }] } }composes引用有三种类型,对应 Rust 侧的 CssModuleReference 枚举:
local:本文件内的局部引用,name是编译后的名字;global:引用全局(不 hash)名字;dependency:引用其他文件导出的名字,带specifier指明依赖文件。
多个类可以一次性组合,用空格分隔:
.logo { composes: bg-indigo padding-large; }在解析实现 src/properties/css_modules.rs 中,composes的值被解析为一系列CustomIdent(遇到from关键字停止),然后可选地跟随from+Specifier。组合的登记逻辑在 src/css_modules.rs 的handle_composes:它要求composes只能出现在单一简单类选择器中,否则返回InvalidComposesSelector错误;对于本地引用,编译后的名字会直接写入导出。
跨文件依赖:composes ... from
composes还可以通过from关键字引用另一个 CSS 文件中定义的类名:
.logo { composes: bg-indigo from './colors.module.css'; }这会输出带有依赖信息的 exports 对象:
{ logo: { name: '8h19c6_logo', isReferenced: false, composes: [{ type: 'dependency', name: 'bg-indigo', specifier: './colors.module.css' }] } }这里有一个重要的分工:使用transformAPI 时,解析这个依赖并应用目标类名是调用方(你的打包器或应用代码)的责任;而使用bundleAPI 时,依赖的解析与内联会自动完成。对应源码中,Specifier::SourceIndex(u32)这一变体正是“捆绑过程中用于标记来源索引”的(见 src/properties/css_modules.rs),bundler 会据此自动展开跨文件组合(见 src/bundler.rs 对 css modules 依赖的收集与内联)。
全局组合:composes ... from global
没有被 hash 的全局类也可以被组合,使用global关键字:
.search { composes: search-widget from global; }对应Specifier::Global变体(src/properties/css_modules.rs),生成的引用类型为global。
全局例外::global伪类
在 CSS module 中,默认所有类选择器和 id 选择器都是局部的。你可以用:global伪类为单个选择器退出局部作用域:
.foo :global(.bar) { color: red; } .foo .bar { color: green; }编译结果为:
.EgL3uq_foo .bar { color: red; } .EgL3uq_foo .EgL3uq_bar { color: #ff0; }可以看到::global(.bar)中的.bar保持原样,而.foo被 hash;第二行中两个类都被 hash。#ff0是green的 minify 压缩结果,说明 CSS modules 与压缩可以同时工作。
局部 CSS 变量:dashedIdents选项
默认情况下,类名、id 选择器、@keyframes、@counter-style的名字以及 CSS grid 的线名和区域名都会被作用域化到定义它们的模块内。CSS 变量以及其他<dashed-ident>名字的作用域化则可以通过dashedIdents选项开启(JS API);CLI 对应--css-modules-dashed-idents标志。
let {code, map, exports} = transform({ // ... cssModules: { dashedIdents: true, }, });开启后,CSS 变量会被重命名,避免与其他文件中的同名变量冲突。引用变量仍使用标准var()语法,Lightning CSS 会自动把它更新为局部作用域的变量名:
:root { --accent-color: hotpink; } .button { background: var(--accent-color); }变为:
:root { --EgL3uq_accent-color: hotpink; } .EgL3uq_button { background: var(--EgL3uq_accent-color); }注意 dashed ident 的重命名同样遵循 pattern(前缀会带上--),这一点可以从 src/css_modules.rs 的add_dashed看到:它把 pattern 应用到去掉--前缀的名字上,再补回--。
你还可以用from关键字引用其他文件中定义的变量:
.button { background: var(--accent-color from './vars.module.css'); }以及用from global引用全局变量:
.button { color: var(--color from global); }同一套语法也适用于其他使用<dashed-ident>语法的 CSS 值。例如,@font-palette-values规则和font-palette属性就用<dashed-ident>来定义与引用自定义字体配色,它们会像 CSS 变量一样被作用域化与引用改写。
在 Rust 配置中,对应的开关是Config.dashed_idents(见 src/css_modules.rs)。跨文件 dashed ident 引用在 src/css_modules.rs 的reference_dashed中实现:对于from 'file'的引用,会生成一个基于“源 hash + 变量名 + specifier”的占位名字,并登记到references映射中,由 bundler 或调用方解析。
自定义命名 pattern
默认情况下,Lightning CSS 会把文件名的 hash 前缀到每个类名和标识符前。你可以用pattern配置(JS API)或--css-modules-pattern(CLI)自定义这个命名规则。
pattern 是一个包含占位符的字符串,Lightning CSS 会按占位符填充,从而支持自定义前缀或调整作用域类名的命名约定:
let {code, map, exports} = transform({ // ... cssModules: { pattern: 'my-company-[name]-[hash]-[local]', }, });当前支持的占位符如下:
| 占位符 | 含义 |
|---|---|
[name] | 文件的基础名(不含扩展名) |
[hash] | 完整文件路径的 hash |
[content-hash] | 文件内容的 hash |
[local] | 原始的类名或标识符 |
这些占位符对应 Rust 侧 Segment 枚举 的Name、Hash、ContentHash、Local四个变体,外加字面量Literal。pattern 的解析在 Pattern::parse:未知占位符会报UnknownPlaceholder错误,未闭合的方括号会报UnclosedBrackets错误。填充逻辑在 Pattern::write,其中[name]会取文件 stem,并把其中的.替换为-(my.module.css的 stem 会变成my-module)。
需要留意的是,[content-hash]只会在打包(bundle)场景中生效:如 src/bundler.rs 所示,只有当 pattern 包含 content-hash 时,bundler 才会为每个源文件计算内容 hash 并填充。
CSS Grid 与 pattern 的注意事项
注意:CSS grid 的线名可能是歧义的,因为浏览器会自动为每个 grid template area 生成以-start和-end结尾的线名。使用 CSS grid 时,你的pattern配置必须以[local]占位符结尾,这些自动生成的线名才能被正确引用。
let { code, map, exports } = transform({ // ... cssModules: { // ❌ [local] 必须在末尾,否则 // 自动生成的 grid 线名无法工作 pattern: '[local]-[hash]' // ✅ 应该这样写 pattern: '[hash]-[local]' } });.grid { grid-template-areas: 'nav main'; } .nav { grid-column-start: nav-start; }Pure 模式
和 webpackcss-loader的pure选项一样,Lightning CSS 也有pure选项,它强制每条规则至少使用一个 id 或类选择器:
let {code, map, exports} = transform({ // ... cssModules: { pure: true, }, });启用后,Lightning CSS 会对没有至少一个 id 或类选择器的 CSS 规则(比如div)直接抛错。这很有用,因为像div这样的选择器不会被作用域化,会影响页面上的所有元素。对应配置字段是 Config.pure,默认关闭(false,见 src/css_modules.rs)。
关闭特性作用域:animation / grid / customIdents / container
grid、动画和自定义标识符的作用域化可以单独关闭。默认情况下,这些全部是开启的:
let {code, map, exports} = transform({ // ... cssModules: { animation: true, grid: true, customIdents: true, }, });此外,TypeScript 定义中还包含container选项,用于控制@container名字的 hash(见 node/index.d.ts);Rust 侧对应的默认配置同样全部为 true(src/css_modules.rs 中的animation、grid、container、custom_idents均默认为 true)。若某个选项设为false,对应的名字将保持原样、不做 hash。
当前未支持的特性
Lightning CSS 目前并未实现其他 CSS modules 实现中的所有特性,其中一些未来可能会被加入:
- 非函数形式的
:local和:global伪类语法(即:local .foo这种老式写法); @value规则——它已被标准 CSS 变量取代;:import与:export这两种 ICSS 规则。
需要时,可以分别通过标准 CSS 变量、var()引用以及导出映射(exports)来替代上述能力。
从 JS 到 Rust 的完整调用链小结
把上文串联起来,一次 CSS modules 转换的完整链路是:
- JS 侧
transform({cssModules: true | config})把配置映射到 Rust 的css_modules::Config(src/css_modules.rs); - 解析样式表时,
Composes属性被解析为名字列表 + 可选的Specifier(src/properties/css_modules.rs); - 打印阶段,
CssModule依据 pattern 计算每个名字的编译名,登记CssModuleExport,并更新var()、composes、grid、keyframes 等引用(src/css_modules.rs); - 结果以 camelCase 结构序列化回 JS 的
exports对象(node/index.d.ts); - 使用
bundleAPI 时,跨文件的composes from与var(--x from ...)依赖会被 bundler 自动解析与内联(src/bundler.rs)。
若你想在 Parcel 中全局启用 CSS modules(而不只针对.module.css文件),可以在package.json中配置@parcel/transformer-css的cssModules选项,或传入一个选项对象(参见 website/pages/docs.md)。CLI 下则使用--css-modules、--css-modules-pattern、--css-modules-dashed-idents三个标志组合(src/main.rs)。
- 前端
- 开发工具
【免费下载链接】lightningcss
An extremely fast CSS parser, transformer, bundler, and minifier written in Rust.
相关推荐
VPS安全维护:Instatic服务器定期检查完整指南
VPS安全维护:Instatic服务器定期检查完整指南 Instatic作为现代化的自托管视觉CMS,在VPS环境中部署后需要通过系统化的定期检查来保障服务器安
CMS后端前端如何用25美元制作开源AI智能眼镜:完整DIY指南
如何用25美元制作开源AI智能眼镜:完整DIY指南 你是否曾梦想拥有一副智能眼镜,却又被数千元的价格标签劝退?现在,通过OpenGlass开源项目,你可以用不到
人工智能AI 应用智能硬件本地部署可穿戴AI AgentGatsby 中使用 CSS Modules:组件级作用域样式实战指南
Gatsby 中使用 CSS Modules:组件级作用域样式实战指南 本指南以 docs/docs/how to/styling/css modules.md
前端静态站点Web框架
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考