BEM要求nav和dropdown必须是两个独立block,nav仅负责语义与布局(如nav__list、nav__item),dropdown自主封装交互逻辑(如dropdown__toggle、dropdown__menu),所有下拉DOM须包裹在dropdown容器内,禁止跨块嵌套或散落于nav__item中。

直接用 .nav-item 或 .menu-link 这类孤立类名,等于主动放弃组件边界——它不说明“这是哪个导航的 item”,也不表明状态归属,后续任何复用、覆盖、调试都会变相增加心智负担。BEM 的价值不在语法本身,而在强制你回答“这个样式属于谁”。
如何确定 block 名称并避免泛义词
block 是整个导航组件的唯一标识,必须反映具体语义和上下文,不能是通用词。比如侧边栏导航、顶部主菜单、面包屑、页脚链接组,它们都叫“导航”,但 block 名应分别为 sidebar-nav、header-nav、breadcrumb、footer-links。
- 禁止使用
nav作为独立 block,它太宽泛,无法隔离样式域 - 若项目中已有命名空间(如
app-),可前置:app-header-nav - block 名必须小写 + 短横线,不允许下划线或驼峰,否则 SASS 函数或构建工具可能解析失败
- 同一页面出现多个导航时,block 名差异必须足够明确,比如
user-nav和admin-nav比nav-1/nav-2更可持续
element 和 modifier 的绑定逻辑不能松动
BEM 的核心约束是:所有 __ 必须挂靠到某个已声明的 block,所有 -- 必须修饰该 block 或其 element,脱离主体的修饰符就是隐患源。
-
sidebar-nav__item合法;__item或nav__item(没声明navblock)非法 -
sidebar-nav__link--active表示“这个导航项下的链接处于激活态”;sidebar-nav--expanded表示“整个导航组件处于展开态” - 避免泛义 modifier,如
--primary、--big;优先用行为或状态描述:--current、--dropdown-open、--has-children - element 不可嵌套:不用
sidebar-nav__item__icon,而应拆成sidebar-nav__item-icon(即 icon 是 item 的一部分,不是 item 的子 element)
在 Sass 中封装 BEM 类名时的关键避坑点
Element Plus 等库用 _bem() 工具函数生成类名,但实际项目里最容易出错的是动态拼接环节。
立即学习“前端免费学习笔记(深入)”;
- 禁止运行时拼接
__或--部分:如`${block}__${elem}`,一旦elem来自 props 且含空格或特殊字符,生成的类名就无效 - 所有 block、element、modifier 字符串必须硬编码,或经严格白名单校验后再参与拼接
- Sass 中推荐用
@function bem()封装,而非重复手写:@return #{$namespace}-#{$block}#{if($element, '__' + $element, '')}#{if($modifier, '--' + $modifier, '')} - 如果使用
clsx(React)或class-variance-authority(Next.js),modifier 列表必须全量枚举,不可用字符串模板生成 key
为什么 CSS Modules 不能替代 BEM 的语义设计
启用 module.css 后,.item 会被编译为 Nav_item__abc123,看似解决了冲突——但它没解决“这个 item 属于哪个导航”的问题。源码中仍可能出现多个 .item,导致语义模糊、协作混乱、重构困难。
- CSS Modules 解决的是运行时作用域,BEM 解决的是人脑中的结构认知
- 即使用了模块化,也建议在 class 名中保留 BEM 结构,如
header-nav__item→ 编译后为HeaderNav_header-nav__item__xyz789,兼顾可读性与隔离性 - 真正容易被忽略的是:BEM 类名是给开发者看的第一层文档,而不是给浏览器执行的最终产物


















