不是bug,是css modules构建时基于模块路径与原始类名生成唯一哈希的设计使然,开发环境因热更新salt变化导致类名变动,生产环境则依赖内容哈希确保稳定。

为什么开发环境刷新后类名变了
这是构建工具默认行为,不是 bug。CSS Modules 的哈希值依赖构建时的随机 salt(如 Webpack 的 webpackHotUpdate 时间戳),每次启动或热更新都会重算,导致 Button_button__abc123 变成 Button_button__def456。
调试时类名跳变,会让 DevTools 断点、手动覆盖样式、甚至截图标注都失效。
- Webpack 用户:在
css-loader的modules.localIdentName中加hashPrefix,例如[path][name]__[local]___[hash:base64:5]+hashPrefix: 'dev',同一文件同一类名每次生成相同哈希 - Vite 用户:在
vite.config.ts的css.modules里配generateScopedName: '[name]__[local]___[hash:base64:5]',并确保没启用dev.ssr导致服务端/客户端哈希不一致 - 注意:生产环境仍应保留随机哈希(如
[hash:base64:8]),避免源码被反推
为什么 SSR 时服务端和浏览器类名对不上
React 或 Next.js 等 SSR 场景下,Node 环境和服务端构建用的哈希 seed 不一致,导致 HTML 里渲染的 button__xyz 和浏览器 hydrate 时计算出的 button__abc 不匹配,触发 React 警告甚至样式丢失。
- 必须让服务端和客户端共享同一套哈希生成逻辑:Webpack 需开启
css-loader的getLocalIdent自定义函数,并传入固定 seed;Vite 则需在build.rollupOptions中统一配置 - Next.js 用户优先用官方推荐的
styled-jsx或className+clsx组合,避免自行处理 CSS Modules 的 SSR 哈希同步 - 若已用自定义
localIdentName,确认服务端构建未误用 development 模式(比如process.env.NODE_ENV === 'development'导致服务端也用了带hashPrefix的规则)
为什么同一个类名在不同组件里哈希结果不同
这其实是正常且关键的设计——CSS Modules 的哈希由「模块路径 + 原始类名」联合计算,Button.module.css 里的 .btn 和 Modal.module.css 里的 .btn 必然生成不同哈希,比如 Button_btn__a1b2c 和 Modal_btn__x9y8z。
- 不要试图“统一”它们:硬要让两个文件的
.btn输出相同类名,等于放弃模块隔离,退化为全局样式 - 若需跨组件复用视觉样式,应提取为独立的 utility class(如
text-center),或通过:global(.text-center)显式声明为全局 - 检查是否误把非 module 文件当 module 用:比如写了
import './Button.css'却期望它有哈希类名——只有.module.css后缀且被正确 loader 处理的文件才生效
为什么改了类名但哈希没变
哈希不变通常是因为构建缓存未清除,或你只改了 JS 中引用的 key(如从 styles.btn 改成 styles.button),但 CSS 文件里还是 .btn { }。
- 真正影响哈希的是 CSS 源文件中的原始类名(
.btn)、文件路径(src/components/Button.module.css)、以及构建配置中的localIdentName模板 - Webpack 用户执行
npx webpack --clean清缓存;Vite 用户删掉node_modules/.vite或加--force参数重启 - 注意:某些 IDE(如 VS Code)的实时预览插件会绕过构建流程直接注入 CSS,此时看到的类名是未哈希的原始名,不具备参考性
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











