双下划线__是bem中唯一合法的块与元素连接符,表示元素直属块而非dom嵌套路径;禁用单下划线_、中划线-及三层__,所有元素必须平级挂靠block,深层结构需重构为新block或并列element。

双下划线 __ 不是嵌套路径,而是直属归属声明
它只表示「这个元素直接属于某个 Block」,不反映 HTML 的 DOM 层级深度。比如 card__title 意味着 title 是 card 这个块的**直属组成部分**,哪怕它在 HTML 里写在 card__header 里面,也不能因此变成 card__header__title。
常见错误现象:user-card__avatar__img 或 form__field__label —— 浏览器不报错,但语义断裂:第二个 __ 让工具无法识别归属,团队成员也无法判断这个 img 到底属于 user-card,还是属于 avatar 这个中间层(而 BEM 根本不承认 avatar 是一个可拥有子元素的“层”)。
- 所有元素必须直接挂靠在 Block 上,不能“套娃”
- 如果视觉上某部分看起来像“元素的子元素”,实际应重构成新 Block(如
avatar)、或提取为并列 Element(如user-card__avatar-img) - Sass 中的
&__插值语法、PostCSS @bem 插件、VS Code 类名跳转,全都依赖 __ 出现在且仅出现在 block 和 element 之间
为什么不能用单下划线 _ 或中划线 - 替代 __
_ 和 - 在 BEM 里有明确分工:- 只用于拼接多词(如 user-card、nav-item),_ 传统上曾被误用于修饰符(如 button_disabled),但现代 BEM 规范已统一要求用 -- 表示 Modifier。
把 card__title 写成 card_title 或 card-title,会导致:
- 解析器无法区分这是 block 名
card+ elementtitle,还是 block 名card_title(整个一坨) - clsx、tailwind-merge 等工具链静默失效,不报错也不警告
- 搜索
card__能精准定位所有 card 相关样式;搜card-会混入card-layout、card-theme等无关结果
双下划线影响 CSS 选择器性能和稳定性
使用 .card__title 这类扁平类名,比写 .card .header h2 或 .card > .content > p 更快匹配,也更耐 HTML 结构变动。
一旦你依赖后代选择器,DOM 微调(比如把 p 包进 div)就会让样式失效;而 BEM 类名绑定到具体元素上,HTML 怎么动,只要 class 不丢,样式就稳。
- 不要写
.card__header h2—— 这等于主动放弃 BEM 的解耦价值 - 也不要用
.card__title同时匹配多个标签(h2和span)再靠标签选择器区分,那会让语义模糊 - 真正需要差异化时,加 Modifier:
.card__title--subtitle
__ 和 -- 混用会破坏状态作用域
__ 绑定结构归属,-- 绑定状态逻辑,二者不可交叉或下沉。例如 card__header--expanded 是危险信号:它暗示 header 可以独立展开,而 card 本身未展开——这在大多数 UI 设计中没有对应语义。
正确做法是让状态落在 Block 层:card--expanded,然后内部所有元素通过该类统一响应,比如:
.card--expanded .card__header { opacity: 1; }
.card--expanded .card__footer { display: block; }
- Modifier 应该回答“这个组件当前是什么状态”,而不是“这个局部看起来怎么样”
- 元素层加 Modifier(如
card__title--highlighted)仅适用于局部、非联动的状态,比如编辑态高亮,而非整体折叠/展开 - 一旦发现多个 Element 都在加同一组 -- 修饰符(如都加
--active),大概率说明该状态本该由 Block 承载
BEM 的双下划线看着只是字符组合,但它卡在工具链解析、团队协作边界、样式可维护性三个关键节点上。漏掉一个 __,可能当时看不出问题,但两个月后当你想批量替换所有 card 相关样式,或者新人接手查不到 user-card_avatar 的定义时,才会意识到——这不是命名习惯,是契约。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











