类名没生效的根本原因是CSS Modules未正确启用:文件名未用.module.css后缀、导入未用import styles from './X.module.css'、styles对象为空或动态拼接混用字面量字符串,导致哈希类名与DOM中实际类名不匹配。

类名没生效,大概率不是写错了选择器,而是 CSS Modules 的类名映射根本没走通——styles 对象为空、className 里写了原始名但实际生成的是哈希名、或者文件压根没被识别为模块。
检查文件名和导入方式是否匹配
CSS Modules 不是“开了开关就自动认所有 .css 文件”。必须满足两个硬性条件:
- 文件后缀必须是
.module.css(或.module.scss等,且构建工具已配对应 loader) - 导入语句必须是
import styles from './Button.module.css',不能是require('./Button.module.css')(后者返回路径字符串) - Webpack 用户要确认
modules配置只作用于/\.module\.css$/,而不是全局/\.css$/,否则第三方样式会被意外模块化 - Vite 用户需检查
vite.config.ts里没显式设css.modules: false
确认 styles 对象是否真有值
在组件里加个 console.log(styles),看输出是不是空对象 {}。如果是:
- Next.js app 目录下:检查组件是否标记了
"use client"—— Server Component 中 import.module.css会静默失败或报错 - TS 项目:确认已配置类型声明,比如
types/css-modules.d.ts,否则 TypeScript 可能允许导入但运行时styles为undefined - 服务端渲染(如 Next.js SSR 或自建 renderToString):
styles在服务端始终是空对象,因为 css-loader 默认不导出 CSS 内容,只导出类名映射;客户端 hydrate 时才补上真实类名,容易导致 FOUC 或 class 名不一致
避免动态拼接时混用 styles 和字面量
写 className={`${styles.btn} ${isPrimary ? 'primary' : ''}` 是典型错误——'primary' 是纯字符串,不会被 CSS Modules 处理,自然没对应样式。
用于 inference.sh 的 JavaScript/TypeScript SDK,可运行 AI 应用、构建代理、集成 150+ 模型。包名:@inferencesh/sdk(npm install),完整 TypeScript 支持。
立即学习“前端免费学习笔记(深入)”;
- 正确写法是全部走
styles对象:className={`${styles.btn} ${isPrimary ? styles.primary : ''}` - 多个条件建议用
clsx:className={clsx(styles.btn, isPrimary && styles.primary, isDisabled && styles.disabled)} -
composes引用时,确保被引用的文件也是.module.css后缀,否则源类名无法被正确解析为哈希名
DevTools 里 class 属性显示对但没样式?查 specificity 和加载顺序
即使 styles 正确、类名也上了 DOM,样式仍可能被覆盖:
- 打开 Elements 面板,选中元素,看右侧 Styles 栏里该类名是否被划掉(strikethrough)——说明被更高优先级规则覆盖
- 检查是否有 ID 选择器、内联
style、或更具体的选择器(如.container .btn)压过了你的模块化类 - 确认框架 CSS(如 Bootstrap)是否在你自己的
.module.css之前加载;后加载的同权重规则会覆盖前者 - 别名路径(如
@/styles/Button.module.css)要确保 Webpack/Vite 别名配置不影响后缀识别,否则模块化流程直接跳过
最常被忽略的一点:CSS Modules 的类名映射是编译时行为,它不改变浏览器解析 CSS 的规则。哪怕 styles.primary 返回了 Button_primary__abc123,如果对应的 CSS 规则没进 HTML <head>(比如 SSR 场景未提取样式),或者被其他规则用更高 specificity 覆盖,它依然不会生效。

















