BEM类名通过强制使用单一类选择器降低CSS解析开销,因浏览器从右向左匹配时,单一类作为关键选择器免去DOM回溯;需避免组合、标签、属性选择器及动态伪类,并注意在CSS-in-JS中防止编译引入属性选择器。

为什么 BEM 类名能降低 CSS 解析开销
浏览器解析 CSS 时,会从右往左匹配选择器(比如 .header__title--large),越靠右的“关键选择器”越影响性能。BEM 强制使用单一、扁平的类名(如 btn、btn--primary、btn__icon),避免嵌套层级和复杂关系符(>、+、~),让关键选择器始终是单个类,省去 DOM 树回溯。
常见错误是把 BEM 当成命名习惯,却仍写 .sidebar .user-card__avatar 这类组合选择器——这直接废掉 BEM 的解析优势。
- 只用单个类名作为选择器:用
.user-card__avatar,不用.sidebar .user-card__avatar - 禁用标签选择器前缀:
div.user-card__avatar比.user-card__avatar多一次标签判断,不必要 - 避免属性选择器用于样式控制:
[data-state="loading"]虽语义好,但比.btn--loading解析慢,非交互态场景优先用 BEM 修饰符
如何改写现有 CSS 以适配 BEM 简单选择器
不是重命名 class 就算 BEM 化,关键是切断选择器依赖链。例如原写法:
.article > .content p:first-child { margin-top: 0; }这段在大型页面中可能触发多次重排,且无法复用。改成 BEM 后应为:
立即学习“前端免费学习笔记(深入)”;
.article__intro { margin-top: 0; }对应 HTML 中显式加类:<p class="article__intro">...。
- 把结构依赖(
>、+)转为语义化修饰符(__intro、__subtitle) - 移除所有
:nth-child()、:first-of-type等动态伪类,它们强制浏览器遍历兄弟节点 - 媒体查询内也保持单类原则:
@media (min-width: 768px) { .nav__item--desktop { ... } },而非.nav .nav__item
BEM 与 CSS-in-JS / Scoped CSS 的兼容要点
在 Vue 的 <style scoped> 或 React 的 Emotion 中,BEM 依然有效,但要注意编译后是否引入属性选择器。
Vue SFC 默认给元素加 data-v-xxx 属性,并重写选择器为 .btn[data-v-abc123]——这会让原本简单的 .btn 变成属性选择器,轻微拖慢解析。可通过配置关闭属性选择器:
// vue.config.js
css: {
extract: false,
loaderOptions: {
css: {
// 避免 scoped 添加 data-* 属性选择器
// 实际需配合 postcss-plugin 或自定义 loader
}
}
}- 推荐用
vue-class-component或 Composition API 显式绑定 BEM 类名,绕过 scoped 自动注入 - Emotion 中避免
css`&__icon { ... }`生成嵌套选择器,改用独立styled.div+ BEM 类名组合 - 若用 Tailwind,BEM 不冲突,但别混用:不要
class="btn btn--primary bg-blue-500",选其一即可
哪些场景下 BEM 简单选择器反而有害
BEM 不是银弹。当组件粒度极小、复用率极低,或需要强样式隔离时,过度 BEM 化会增加维护成本,甚至引入冗余类名。
典型反模式:
- 为每个
<span>加text__highlight类——不如用.text--highlight更通用 - 在工具类库中硬套 BEM:
flex__container、grid__item,违背工具类设计初衷 - 服务端渲染(SSR)页面中,BEM 类名未预加载到 JS bundle,导致首屏样式闪烁(CLS)——此时需确保 CSS 提前内联或预加载
真正起效的前提是:CSS 文件体积可控、类名无重复、开发者严格遵守“一个元素一个 BEM 根类”的约束。漏掉任意一点,解析优势都会被抵消。



















