BEM是强制从首行CSS起就遵循的结构化命名语言,而非事后补救工具;它通过.block__element--modifier格式天然隔离样式、明确归属与状态,杜绝全局冲突和后代选择器依赖。

BEM 不是用来“修”混乱命名的急救包,而是从写第一行 CSS 开始就该用的结构语言——它不靠工具自动补救,靠的是类名本身携带归属、层级和状态信息。
为什么 .btn 会覆盖 .user-card__avatar
CSS 是全局作用域,浏览器只认类名字符串,不认你心里想的“这个 btn 是弹窗里的”。只要两个文件都定义了 .btn,后加载的规则就赢;而 .user-card__avatar 和 .profile-card__avatar 天然不同名,哪怕共用同一份 CSS 文件也不会互相干扰。
-
.btn:hover在 A 模块改背景色,B 模块的按钮也会变——因为没上下文绑定 -
.card .title这种后代选择器依赖 DOM 结构,HTML 里加个 wrapper 就失效;正确写法是.card__title,扁平、自解释、不靠父级 - 构建工具(如 css-loader + modules)能把
.button__icon编译成_button__icon_abc123,但 BEM 结构保留在源码和 DevTools 中,调试时仍能一眼看出这是按钮里的图标
怎样写才算真 BEM,而不是伪 BEM
常见伪 BEM:类名带双下划线却没 block 上下文,或 modifier 脱离 block 单独使用。这类写法失去隔离能力,调试时根本看不出样式来源。
-
search-form__input--disabled✅(search-form是 block,input是其元素,disabled是状态) -
search-form__input_error❌(单下划线非标准,应为--error) -
button--primary❌(缺少 block 前缀,button不是合法 block 名,应为checkout-button--primary) -
header__nav-item_active❌(nav-item是另一个组件,不该塞进header的命名空间)
React/Vue 里拼 className 容易踩哪些坑
手拼字符串看着快,实际极易漏空格、错连字符、大小写混用,而且构建环境对大小写敏感度不同,本地跑得通,CI 上直接挂。
PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。
立即学习“前端免费学习笔记(深入)”;
- ❌ 危险写法:
className={`button button--${variant} ${hasIcon ? 'button__icon' : ''}`}(缺空格、无防错、button__icon脱离 block) - ✅ 推荐用
clsx或封装常量:const BLOCK = 'search-form'; const cn = (e, m) => `${BLOCK}${e ? '__' + e : ''}${m ? '--' + m : ''}` - 禁止在 JSX 中硬编码哈希后的类名(如
className="_button_abc123"),那等于放弃 CSS Modules 的可维护性 - Modifier 必须与 block/element 并列使用,不是子类:
button--primary button--disabled正确,button--primary--disabled是反模式
老项目怎么渐进启用 BEM,而不是推倒重来
全局搜索替换 .btn 为 button__text 是高危操作——旧项目里 .btn 可能同时承担按钮、链接、标签三种语义,还混着 .btn-red、.active-btn 这类非结构化命名,强行统一会导致 HTML 失效、JS 绑定断裂、语义失准。
- 真正可行的起点是:只对新增组件、高频修改区域、或已明确复用失败的模块启用 BEM,比如弹窗、表单校验提示、数据表格操作栏
- 给旧组件加 wrapper class,比如把遗留的
.nav包进<nav class="header-nav"></nav>,后续扩展用header-nav__item - 在团队 ESLint 配置中加
stylelint-selector-bem-pattern规则,只对新写的.scss或.css文件生效 - Block 名必须是独立、可复用的功能单元(如
search-form),不能是section-2或left-sidebar这类带位置词的伪 Block
最容易被忽略的一点:BEM 不是给机器看的,是给人看的。很多团队开了 CSS Modules 就以为万事大吉,结果组件目录里全是 styles.css、index.module.scss,连文件名都看不出对应哪个 UI 元素。BEM 的真正价值,在于让文件命名、目录结构、类名三者之间建立一致映射——比如 components/Button/Button.module.css 里只允许出现 button、button__*、button--* 开头的类名。一旦打破这个约束,再好的工具也救不了语义断层。

















