composes语法用于CSS Modules编译期类名组合,必须写在规则体最前面,仅支持单类名(如.btn),跨文件需composes: cls from './file.module.css',路径须准确且被引用类须为顶层声明。

composes语法的基本写法和作用范围
composes 是 CSS Modules 提供的类名组合机制,它不是 CSS 原生特性,而是由构建工具(如 webpack 的 css-loader)在编译时处理的。它只在同一个 CSS 文件内或跨文件导入时生效,不会在运行时起作用。
- 必须写在类选择器内部,不能放在伪类、媒体查询或嵌套规则里
- 只能组合其他类名(
.btn),不能组合标签名、ID 或属性选择器 - 跨文件使用时,需用
composes: className from './path.css'显式导入 - 组合顺序影响最终样式优先级:后
composes的类声明会覆盖前者的同名属性
例如:
.primary {
composes: btn from './base.css';
background-color: #007bff;
}
跨文件复用时路径和导出必须匹配
CSS Modules 默认只导出顶层类名,不自动导出被 composes 引用的类——除非那个类本身是顶层声明(即以 .xxx { } 形式存在)。常见错误是试图 composes: helper from './utils.css',但 utils.css 里只有 @media 规则或未导出的局部类。
- 确保被复用的类在源文件中是独立的顶层声明,比如:
.flex-center { display: flex; align-items: center; justify-content: center; } - 导入路径必须相对于当前文件,且支持别名(如
~styles/utils.css),但别名需在 webpack 配置中提前定义 - 如果使用
export关键字(如:export { themeColor: #333; }),它和composes无关,不能被组合
与 :global() 混用时的优先级陷阱
composes 和 :global() 可以共存,但容易误判样式来源。比如:
.card {
composes: shadow from './base.css';
composes: global-reset from ':global(.reset)';
}
-
:global(.reset)不经过模块化哈希,其样式直接进入全局作用域 - 若
shadow和.reset有冲突属性(如margin),实际生效的是 CSS 层叠规则,而非composes的书写顺序 - 更危险的是:如果
.reset在其他地方被重写(比如另一个 CSS 文件也定义了.reset),你的组件可能突然“失重”
所以,尽量避免对 :global() 类做 composes,真要复用全局样式,建议封装成独立的模块类再导入。
构建配置必须启用 css-loader 的 modules 选项
composes 功能完全依赖 css-loader 的解析逻辑。如果 webpack 配置中漏掉 modules: true 或用了旧版 css-loader@5- 的非对象式配置,composes 会被当成无效声明忽略,连警告都不会抛。
立即学习“前端免费学习笔记(深入)”;
- 推荐配置写法(webpack v5+):
{ loader: 'css-loader', options: { modules: { auto: true, // 或指定正则匹配 .module.css } } } - 使用 Vite 时需确认启用了
css.modules(默认开启,但若关闭了css.modules.generateScopedName,composes仍可用,只是类名不哈希) - Next.js 13+ App Router 中,
.module.css文件默认启用模块化,但composes仅在该文件内有效,跨文件需显式from
真正容易被忽略的是:composes 不产生运行时开销,但它的复用链一旦变长(A → B → C → D),调试时很难一眼看出某个样式究竟来自哪一层——得靠构建后生成的映射文件或浏览器开发者工具里的原始文件链接来逐层跳转。


















