BEM是一种约束组件结构的CSS命名契约,核心为三段式.block__element--modifier:block须具业务语义(如user-card),element仅属单一block(禁用嵌套如user-card__avatar__icon),modifier仅表状态(如btn--loading,不表位置或层级)。

什么是BEM命名规范
BEM不是字符串拼接规则,而是一套约束组件结构的CSS命名契约。它强制把样式作用域收敛到「块」这一最小可复用单元,避免靠
核心是三段式:.block__element--modifier。其中block必须带业务语义(如user-card),不能是header或section这类HTML标签名;element只能属于且仅属于一个block(user-card__avatar-icon✅,user-card__avatar__icon❌);modifier只表达状态开关(btn--loading✅),不表达位置或层级(btn--in-modal❌)。
为什么直接套用header__logo--dark会出问题
因为header不是业务块,而是泛容器名。当页面出现main-header和modal-header两个模块时,它们的__logo语义就冲突了——BEM要求每个block必须是独立、可复用、有明确边界的组件。
- 常见错误:把
div.header > div.logo直接转成header__logo,结果在另一个模块复用logo时,样式被意外污染 - 正确做法:提取业务含义,比如登录页的logo叫
login-form__logo,用户页的叫user-profile__logo - 第三方UI库(如Ant Design)不强行改名,但在外层包一层
my-dashboard__ant-table-wrapper做隔离
React/Vue里怎么写才不乱
动态类名拼接是最容易踩坑的地方,手写className={`card ${isFeatured ? 'card--featured' : ''}`}极易漏前缀、错连字符、大小写混用。
立即学习“前端免费学习笔记(深入)”;
- 用
clsx代替三元拼接:className={clsx('card', { 'card--featured': isFeatured })} - 禁止在组件内定义常量类名,比如
const headerClass = 'card__header',这会让DOM审查时丢失语义 - 如果用了CSS Modules,确保
:local(.card__header)和实际类名完全一致,否则哈希化后BEM可读性就没了 - 修饰符叠加顺序按“类型→状态”排:
btn--large btn--loading,不是btn--loading-large
嵌套深、组件多时怎么避免类名爆炸
比如user-card里有个avatar,而avatar本身又是独立可复用组件。这时候不能写user-card__avatar__image,也不能把avatar整个CSS搬进去。
- 正确做法:把
avatar升格为独立block,命名为avatar,在user-card里用user-card__avatar作为容器,内部再挂avatar的完整类名 - 轮播图指示器不能抽象成
dot块,必须是carousel__indicator——它是轮播图的子功能单元,不是独立组件 - 垂直轮播不该用
carousel--vertical修饰符,而应另起块名carousel-vertical,因为交互逻辑、指示器排布、分页方式都完全不同
真正难的从来不是记住__和--怎么用,而是每次加新类名前,先问一句:这个东西到底是不是一个独立、可复用、有明确定义边界的业务块?


















