block 必须保持功能独立与样式内聚,仅定义 padding、border、color 等交互相关属性;外边距、尺寸、定位等布局职责应由父容器或布局类控制,否则破坏封装性、导致权重失控与复用失效。

直接修改 Block 样式(比如给 .button 加 margin 或 width: 100%)会破坏组件封装性,因为它把布局职责塞进了原子级 UI 组件内部。
Block 必须是功能独立、可复用的最小单元
BEM 的 block 不是“视觉容器”,而是有明确职责的抽象:比如 .button 负责点击行为和基础交互样式,.card 负责内容卡片的边框、内边距和阴影。一旦你在 .button 里写 margin-bottom: 1rem,它就不再是“按钮”,而是一个“带底边距的按钮”——这个边距在网格布局、表单行内、弹窗 footer 等场景下大概率不适用。
- 所有影响外部间距、尺寸、定位的样式,应由父容器或布局类(如
.form-group、.grid-col)控制 - Block 自身只定义内聚样式:padding、border、color、font、cursor、transition 等与交互/视觉表现强相关的属性
- 如果某个 Block 在多个地方需要统一外边距,说明它其实该被包裹进一个更高阶的 Block(比如
.button-wrapper),而不是污染原组件
修改 Block 样式会导致 CSS 权重失控和覆盖链断裂
当你在项目后期发现 .button 的 margin 需要动态调整,就得加 !important、写更具体的选择器(如 .modal .button),甚至改 JS 切换逻辑——这违背了 BEM “类名即状态” 的设计初衷。
- 修饰符(
--modifier)只应表达语义化变体,比如.button--large改的是 padding 和 font-size,不是 margin - 布局类(如
.spacing-xs、.text-center)必须和 Block 类名正交,靠组合生效:<button class="button button--primary spacing-mb"></button> - 预处理器中禁止用嵌套生成后代选择器来“修正” Block 外观,例如
.form { .button { margin-top: 0.5rem; } }—— 这会让.button在.form外失效,也破坏了 Block 的可移植性
React/Vue 中最容易踩的坑:把 Block 当布局锚点
很多团队在组件里写 className="button",却在父组件里用 style={{ marginTop: '1rem' }} 或 class="flex items-center gap-2" 去“摆布”它,这等于用运行时逻辑替代命名契约,BEM 的语义就断了。
- 真正符合 BEM 封装原则的做法是:每个 Block 渲染前,由其调用方决定上下文,比如
<button classname="spacing-mb"></button>或<div class="form-field"><button></button></div> - 如果 Block 内部硬编码了
display: inline-flex,那它就无法被安全地放进grid或table布局中;正确做法是默认display: inline,让父级用flex类接管流式行为 - 使用 CSS Modules 或
clsx时,避免在组件内部拼接布局类:className={`${base} ${props.className || ''}`}是 OK 的,但className={`${base} ${props.size === 'block' ? 'w-full' : ''}`}就越界了——尺寸语义应该交给--full-width修饰符,而不是混入布局逻辑
BEM 封装性的核心不在“能不能改”,而在“谁有权改、为什么改、改了之后还能不能独立存在”。Block 类名一旦承担布局责任,它就不再是组件,而是耦合体——下次重构 DOM 结构时,你得翻遍所有用了 .button 的地方,挨个检查 margin 是否还合理。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











