bem重构核心是语义归属与性能优化:所有选择器必须显式绑定block,禁用多层后代、孤立类名及高成本伪类,确保可维护性与低权重。

遇到 .header .nav .item a 这类选择器必须重写
这种多层后代选择器不是“写法不够优雅”,而是运行时性能隐患 + 维护灾难。浏览器从右往左匹配时,会为每个 a 回溯父级节点,DOM越深、元素越多,重排重绘开销越明显;更现实的问题是:改一个 .nav 类名,整条规则就失效,但你未必立刻发现——它可能只影响某个弹窗里的下拉菜单。
重构不是简单删掉空格,而是绑定归属:
- 先确认这个
a属于哪个功能闭环单元(比如是导航项,不是通用链接),把它提升为 Block 或归入已有 Block(如main-nav) - 把
.item a改成main-nav__item,链接本身加main-nav__link - CSS 中删除所有
.header .nav .item a规则,只保留.main-nav__link和.main-nav__link:hover - 禁止用
.main-nav__item .main-nav__link这种后代写法——它绕过 BEM 隔离,且未来无法迁移到 CSS Modules
.card .title 和 .card-title 都不合规,正确写法是 .card__title
前者是深层嵌套,后者是“伪BEM”:没体现归属关系,.card-title 无法回答“这个 title 是哪个 card 的?是不是复用了别的 card 的样式?”
真正合规的写法必须显式携带 Block 名:
- 如果 title 只属于
product-card,就写.product-card__title - 如果 title 同时出现在
user-card和cart-card,说明它具备独立语义,应拆成新 Block:card-title-block,而非强行共用.card__title - Element 名不能脱离 Block 单独存在,所以禁止
.title或.card-title这类孤立类名 - 不要为了省字符写
.card__t——可读性比长度重要,调试时没人能猜出 t 代表什么
审查时发现 button:hover 或 input:focus 怎么办
伪类本身没问题,BEM 不禁止 .button:hover,但禁止用嵌套语法生成它(如 Sass 的 &:hover 块)。问题在于:如果原始 HTML 没写 class="button",只写了 class="button--primary",那 .button:hover 规则就完全不生效。
安全写法只有两种:
- 确保基础 Block 类始终存在:
class="button button--primary",然后写.button:hover和.button--primary:hover - 若必须用 Element 级交互(如
.button__icon单独 hover),则 modifier 必须带 Block 前缀:.button__icon--hovered,而不是.button__icon:hover(后者在 SSR 或 JS 动态增删 class 时容易漏控) - 避免用
:focus-within或:has()这类高成本选择器——它们会强制浏览器遍历子树,BEM 的扁平结构本可规避这类开销
重构后 CSS 权重还是被旧样式压住?先查 #header 和 !important
不是 BEM 写错了,是旧 CSS 权重太高。BEM 类名天然低权重(纯 class),一旦遇到 ID 选择器或 !important,就会被压制。
快速定位方法:
- 在开发者工具中选中元素,看 computed 样式里哪条规则被划掉,点开它,往上翻看 source 行号——大概率来自 reset.css、legacy.css 或某处手写的
#main-nav - 全局搜索
id=和!important,优先降权:把#header改成.site-header,把!important替换为更具体的 BEM 类(如.header--fixed) - 禁用所有非 BEM 工具类(如
.clearfix)的直接使用,统一收口到u-clearfix命名空间下,和业务类严格分离
最常被忽略的一点:BEM 的有效性不取决于名字多长,而取决于每次加新类名时,是否真在回答“这个类脱离当前文件,别人能看懂它属于谁、起什么作用吗?”——没问这个问题,重构只是把混乱换了个更长的名字。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











