真正起作用的是显式拼接出带上下文的类名,让名字本身就携带归属信息,脱离 dom 层级依赖;bem 与 css modules 需严格遵循命名规范和边界控制才能实现物理隔离。

直接用 .btn、.title 这类泛化类名,必然污染全局——浏览器不认“谁写的”,只按选择器是否匹配、权重高低、加载顺序来决定样式生效与否。
为什么嵌套写法(如 .card { .title { } })根本防不住冲突
Less/Sass 的嵌套只是语法糖,编译后生成的是后代选择器 .card .title,不是独立类名。只要页面里其他地方有 .title,照样被覆盖或反向影响。
- 你写
.user-card { .title { color: blue; } },但全局 CSS 里有一条.title { color: red; },且后者后加载,你的蓝色就失效 - HTML 结构一变(比如加了个
<div class="wrapper">),<code>.user-card .title就可能匹配不到目标元素 - 第三方组件内部 DOM 不可控,
.ant-modal .title这种写法会误中你自己的.title - 正确写法:
.search-form { &__input { } &--loading { } }→ 编译为.search-form__input、.search-form--loading -
&必须紧贴选择器,& __input(中间空格)会变成全局__input,完全失效 - Block 名必须真实存在于 HTML 中:
<div class="search-form">,不能是抽象词如 <code>ui或base - Element 名要角色明确:
search-form__submit✅,search-form__wrapper❌(wrapper 不是语义角色) - 动态拼接类名漏下划线:
cls + '__icon'写成cls + 'icon',结果生成buttonicon,而模块实际输出的是button__icon_abc123 -
:global(.ant-input)没加限定范围,导致所有.ant-input都被你重置,包括其他页面的 - 第三方组件用
dangerouslySetInnerHTML插入的 HTML 带class="highlight",这类类名不会被 Modules 处理,也不会加哈希 - 给外层容器加唯一属性:
<div data-scope="user-profile">,再写 <code>[data-scope="user-profile"] .ant-input - 避免模糊匹配:
[class*="ant-"]会误伤你自己写的my-ant-design-tweak,优先用[class^="ant-"] - 服务端渲染时若无法改 class,用 BEM 块名兜底:
.user-profile [class^="hljs-"],既安全又可维护
用 & 拼出 BEM 类名才是物理隔离
真正起作用的是显式拼接出带上下文的类名,让名字本身就携带归属信息,脱离 DOM 层级依赖。
CSS Modules 启用后仍泄漏的三个高频原因
开了 modules: true 不等于自动免疫,泄漏基本都发生在边界操作上。
第三方 UI 库(如 Ant Design)样式隔离最务实的做法
别试图用 BEM 或 Modules 覆盖 .ant-*,它们不可变;你唯一能控制的是“作用域入口”。
真正麻烦的不是规则本身,而是总有人觉得“就改一行样式,没必要套三层”。等上线后发现 card__title 被另一处 article-title 的 margin 撑开,才明白:隔离效果不取决于你写得多快,而取决于类名是否天然绑定到具体功能块。











