bem必须严格遵循block__element--modifier结构,因其通过单类名实现一次哈希查找提升性能,并在类名中内嵌归属、角色、状态三层语义以保障作用域隔离与可维护性。

BEM不是“黄金标准”这种带价值判断的营销话术,而是当项目达到一定规模后,唯一能守住CSS作用域底线的命名体系——它不解决所有问题,但一旦缺失,样式泄漏、协作混乱、组件复用失败就会立刻发生。
为什么BEM类名必须严格遵循block__element--modifier结构
这不是为了形式统一,而是让浏览器和人都能一次定位作用域。单类名选择器(如.user-card__avatar--xs)在CSS匹配时只需哈希表查一次;而.user-card .avatar这类嵌套写法会触发DOM树遍历,层级越深性能越差。更重要的是,类名里自带归属(user-card)、角色(avatar)、状态(xs)三层语义,开发者看HTML就能反推组件结构,不用点开JS或CSS文件。
常见错误现象:
-
card__title被误用在非card容器中,导致样式错位且无法被CI工具识别 - 手写
header__logo__icon,违反“Element不能嵌套Element”原则,应拆成header__logo+logo__icon(后者升格为Block) - Sass中误用
& .card__subtitle,编译出带空格的选择器,破坏BEM语义隔离
修饰符为什么不能写成button--width-200px或is-loading
修饰符不是样式开关集合,而是设计契约中的可预测变体。把具体值(如200px)塞进类名,换单位、加媒体查询就得新增一堆类;用is-前缀则混淆了运行时状态与设计态变体——is-open可能被JS反复增删,而modal__overlay--visible是模板决定的稳定契约。
正确做法:
- 描述“变化”而非“条件”:
--loading✅,--is-loading❌ - 多修饰符共存但独立:
button--primary button--large button--disabled,而非button--primary--large--disabled - 响应式修饰符按变化范围归属:
product-card--mobile(整块布局切换) vsbutton__icon--right(仅元素自身微调)
为什么团队落地BEM最常破功在“伪BEM”和手动拼接类名
光有命名格式没用。真实项目里,class="card card--featured"却只定义了.card--featured,漏掉基础.card样式,删掉修饰符整个组件就崩——这是典型的伪BEM。更隐蔽的是JS中手动拼接:className={`card__body ${isCollapsed ? 'card__body--collapsed' : ''}`},既易错又难维护。
必须靠工具链焊死:
- 用
stylelint-selector-bem-pattern拦截.header .logo或.btn-primary等非BEM写法 - 禁止在JS中硬编码类名,改用
cn('card__body', { 'card__body--collapsed': isCollapsed })(配合clsx) - 目录结构强制对齐:每个
.user-card__avatar必须对应/components/user-card/目录,CI阶段用grep -r "user-card__avatar" src/ | grep -v "user-card/"揪出跨块引用
真正难的不是写出block__element--modifier,而是让团队在加一个tooltip时,本能地新建/components/tooltip/,而不是往modal.css里塞.modal__tooltip——这需要目录约束、CI校验、以及最初几个模块的示范性实现。否则BEM很快退化成“带下划线的普通CSS”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











