CSS模块化本质是作用域隔离,需构建工具配置modules: true并正确导入;文件名匹配.module.css、区分全局/模块规则、JS中通过styles.xxx引用类名,嵌套与动画需手动处理。
module 后缀就完事了CSS 模块化本质是**作用域隔离**,目标是让每个组件的样式只影响自己,不污染全局。常见误解是以为给文件起名 Button.module.css 就自动模块化了——实际必须配合构建工具(如 Webpack、Vite)和正确的导入方式才能生效。原生 CSS 无模块机制,:scope 或 @layer 也解决不了组件级封装问题。
在 webpack.config.js 的 module.rules 里,必须显式启用 modules: true,且不能与全局 CSS 规则混用:
module: {
rules: [
{
test: /\.module\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true, // 必须开启
localsConvention: 'camelCase' // 可选:支持驼峰命名引用
}
}
]
},
{
test: /\.css$/,
exclude: /\.module\.css$/,
use: ['style-loader', 'css-loader'] // 全局 CSS 单独处理
}
]
}
test: /\.module\.css$/ 是触发模块化的开关,文件名必须匹配exclude 会导致全局 CSS 被误判为模块,class 名被哈希化,页面样式崩坏localsConvention: 'camelCase' 允许你在 JS 中写 styles.buttonPrimary 而非 styles['button-primary']
导入时必须用 import styles from './Button.module.css',不能用 import './Button.module.css'(后者会加载但不返回类名映射):
import React from 'react';
import styles from './Button.module.css';
const Button = ({ children }) => (
);
export default Button;

styles.xxx 引用,硬编码 className="button" 会失效className={`${styles.button} ${styles.primary}`} 或 clsx 库styles[status] 这种写法只有在 localsConvention 开启时才安全CSS Modules 默认只作用于顶层选择器,对 & 嵌套、@keyframes 和伪类仍需手动处理:
&:hover 有效,但 .parent & 会脱离模块作用域,变*局污染@keyframes 名称不会被模块化,必须手动加前缀或改用 animationName 动态注入:global(.third-party-button) 显式声明,否则无效模块化不是银弹,它把「命名冲突」问题转成了「引用繁琐」和「动态样式管理」问题,真正提升可维护性的关键是团队约定 + 工具链统一 + 对副作用保持敏感。