BEM修饰符应表达状态或角色而非视觉结果,避免语义与样式强绑定;元素层级勿超两层,禁用多重修饰符组合;用:where()归一化基础样式,动态生成类名须校验过滤。

用 BEM 命名但不用 --modifier 写死样式
直接写 .btn--primary 看似规范,实际把语义和视觉强绑定了:换主题时要改所有类名,加新状态得同步补 CSS。真正复用的关键是让修饰符只表达「状态」或「角色」,不描述颜色、尺寸等视觉结果。
-
--disabled合理(描述交互状态),--blue不合理(描述视觉结果) - 把视觉规则抽到单独的工具类或主题变量里,比如
.is-disabled控制指针和透明度,.theme-dark .btn负责颜色 - 避免在 BEM 元素上叠加多个修饰符来组合效果,如
.btn--primary--large--disabled—— 这本质是把样式逻辑塞进 HTML,后期难维护
元素层级嵌套超过两层就该警惕耦合风险
BEM 的 __element 本意是组件内不可拆分的原子节点,不是任意 DOM 深度的产物。一旦出现 .card__content__title__link,说明结构已失控。
- 检查是否真需要三层语义:多数情况
.card__title-link就够了,__title-link是合法的元素名 - 如果子元素确实需独立样式(比如弹窗里的按钮要特殊边距),优先用上下文选择器
.modal .btn或新增修饰符.btn--in-modal,而非加深 BEM 层级 - Webpack 或 Vite 中开启 CSS scope 检查插件,能自动告警嵌套超限的
__链
:where() 和 :is() 配合 BEM 减少重复声明
传统 BEM 经常为不同块写重复属性,比如 .header__logo 和 .footer__logo 都要设 max-width: 120px。用现代伪类可以安全合并。
- 用
:where(.header__logo, .footer__logo)统一设置基础尺寸,权重为 0,不干扰后续修饰符覆盖 - 避免用
:is()做跨块归一化,它会提升权重,可能导致.btn--primary覆盖失败 - 注意 Safari 15.4+ 才完全支持
:where(),若需兼容旧版,用 PostCSS 插件降级为多行声明
动态生成修饰符类名时别拼错 -- 或漏转义
JS 拼接类名(如 React 的 className={`${base}--${mod}`})是最容易出错的环节:字符串拼错、mod 为空、特殊字符没过滤,都会导致样式失效且无报错。
立即学习“前端免费学习笔记(深入)”;
- 永远校验
mod是否为非空字符串,空值时返回原base,不要留.btn--这种无效类 - 用正则过滤
mod中的空格、斜杠、点号:mod.replace(/[^a-z0-9-]/g, '') - Vue 的
:class或 React 的clsx库比手拼更可靠,但要注意clsx('btn', `btn--${mod}`)在mod为空时仍会输出btn--
--hover 的行为,可能影响五个页面的交互反馈逻辑。


















