bem命名不是格式要求,而是作用域声明;它通过block__element--modifier三段式类名强制划清模块边界,使每个类名自带归属、角色与状态上下文,从而避免全局污染。

直接用 .class 选择器做模块化开发,不加约束就是自埋雷——它本身不具备作用域,全局生效,多人协作时改一个 .btn 可能让整个后台管理页的按钮全崩。
为什么单纯写 .card 不算模块化
类选择器天生是全局的,.card 在任何地方被定义,就会覆盖所有同名元素。哪怕你把样式拆成 card.css 和 header.css 两个文件,只要都引入了页面,.card__title 就可能被 header.css 里一条更宽泛的 .card h2 意外劫持。
- 常见错误现象:复制一个组件到新页面,标题字体突然变小——其实是另一处
.card h2 { font-size: 14px }生效了 - 性能影响:浏览器要遍历全部 DOM 匹配每个
.card,嵌套越深(如.sidebar .card .card__body p),重排重绘成本越高 - 根本问题不在“怎么写”,而在“谁有权用这个名字”——没有边界,就没有模块
BEM 命名不是格式要求,是作用域声明
BEM 的 block__element--modifier 结构,本质是用命名强制划清模块边界。它不改变 CSS 运行机制,但让每个类名自带上下文。
-
.search-form是 Block,代表一个完整功能单元;.search-form__input是它的直属子节点,不能出现.search-form__input__icon - 修饰符必须依附于 Block 或 Element:
.button--primary合法,.button--top违规——位置属于布局职责,应由父容器控制 - 避免泛命名:
.content、.wrapper这类名字无法回答“这是谁?属于谁?”,容易在合并 PR 时冲突
CSS Modules 才是 class 选择器真正落地模块化的前提
光靠 BEM 命名解决不了全局污染。只有配合构建工具,让 .card__title 编译后变成 card_card__title_abc123,才真正隔离。
- Webpack 中需启用
css-loader的modules: true,且localIdentName必须带 hash,否则热更新时类名不变,调试找不到源码 - HTML 中不能硬写
class="card__title"——编译后失效,必须通过 JS import:import styles from './Card.module.css'; <h2 classname="{styles.card__title}"></h2> - 纯静态页无 JS 入口?放弃 CSS Modules,改用命名空间前缀 + PostCSS 自动加前缀,例如
.u-card__title,并确保构建流程统一处理
复用模块时,HTML 和 class 名必须同步迁移
把一个 <div class="product-card"> 复制到新页面,却漏掉对应的 <code>.product-card__price 样式规则,结果价格显示错位——这不是 bug,是模块契约被破坏。
- 每个模块对外暴露的 class 名就是它的 API,改名=破接口,必须同步更新所有引用处
- 构建时开启
purgeCSS或 Tailwind 的purge配置,能自动剔除未使用的.product-card__badge,但前提是 HTML 中真实存在该 class - 最易忽略的一点:BEM 类名一旦进入 CSS Modules 编译流程,就再不能被外部样式覆盖——想留钩子?得显式导出
:global(.legacy-override),且仅限极少数兼容场景











