为何在现代CSS3开发中依然推荐使用BEM命名法?

作者:袖梨 2026-07-04
BEM选择器更快是因为单类名(如.user-card__avatar--loading)支持浏览器一次哈希查找,而嵌套选择器(如.user-card .avatar)需从右往左逐层回溯父节点,DOM越深性能差距越显著。

因为BEM不是“过时的命名习惯”,而是解决 CSS 类名语义模糊、作用域失控、协作成本飙升这三类问题的最小可行方案——它不依赖构建工具,不绑定框架,也不靠运行时魔法,只靠命名本身把“谁的、什么、什么状态”三个问题塞进类名里。

为什么 BEM 选择器在现代浏览器里依然比嵌套选择器快

BEM 的性能优势不来自名字长短,而来自单类名选择器一次哈希查找。比如 .user-card__avatar--loading 是浏览器直接查 class 表;而 .user-card .avatar 会让浏览器先找所有 .avatar 元素,再逐层向上检查父级是否匹配 .user-card,DOM 越深越慢。

  • DevTools → Elements → Computed → Styles 面板里一眼就能看出有没有空格——有空格就是潜在性能隐患
  • Sass 中写 .user-card { &__avatar { } } 安全;但写 .user-card { & .user-card__badge { } } 就会编译出带空格的选择器
  • 构建后用 grep -r ".[a-z]+ .[a-z]" dist/ 快速揪出残留的空格选择器

为什么 React/Vue 组件结构天然适配 BEM 命名

.user-card 对应 UserCard.vueUserCard.jsx,Block 名就是组件名直译,目录、文件、类名三者一致,新人看类名就能反推组件位置。

  • 没有 .card .header 这类嵌套选择器,父组件 DOM 结构微调(比如加 wrapper)不会导致子组件样式失效
  • 第三方库或 SSR 直出 HTML 时,JS 还没执行,user-card__avatar 这种类名就是唯一契约,不靠哈希、不靠 scope 属性
  • 微前端场景下,payment-button__icon--disabled 天然带命名空间,不会和宿主项目冲突

BEM 最容易被绕开的三个破功点

规范写得再好,只要这三点没守住,BEM 就退化成“带下划线的普通 CSS”:

立即学习“前端免费学习笔记(深入)”;

  • 伪BEM:HTML 写 class="user-card user-card--loading",但 CSS 只定义了 .user-card--loading,漏掉基础块样式,删 modifier 整个组件就崩
  • 跨 Block 的 Element:把 user-card__avatar 放到非 user-card 容器里,或写成 user-card__header__title(BEM 不允许 Element 嵌套 Element)
  • 修饰符滥用:写 button--width-200px 这类含具体值的类名,换单位或加媒体查询就得新增一堆类,违背“修饰符描述状态”的原则

真正难的不是写对一个类名,而是每次写 __-- 时都得问自己:这个元素是不是它所属 Block 的固有部分?这个修饰符是不是可枚举、可复用、不带硬编码值的状态?——这种判断没法自动化,只能靠人盯住。

相关文章

精彩推荐