bem 的核心是切断样式耦合,而非加长类名;所有组件级样式必须以块名开头,元素和修饰符需严格遵循规则,通过协作契约与工具辅助实现低成本落地。

为什么改一个按钮样式会牵连整个导航栏
因为没用 BEM,类名语义模糊、层级嵌套过深,导致 CSS 选择器依赖 DOM 结构。一旦 HTML 微调,.nav .item a:hover 就可能失效,或者意外覆盖其他模块的 a 标签。
真正该做的,是让每个样式只属于它自己负责的模块,不猜上下文、不靠父子关系“借力”。BEM 的核心不是加长类名,而是切断样式耦合。
- 所有组件级样式必须以
BEM 块名开头,比如button模块只能用button__icon、button--primary,绝不用.header .button - 子元素(element)必须是块的直接组成部分,不能跨块复用,比如
modal__close和toast__close是两个独立类,哪怕长得一样 - 修饰符(modifier)只表达状态或变体,不承担布局职责——
button--disabled可以设opacity和cursor,但不该在里面写margin-left: 20px
怎么给现有项目低成本接入 BEM
别重写,先堵漏。从高频修改、多人协作的模块开始,比如表单、卡片、弹窗。重点不是全量替换,而是建立“新代码必须 BEM,老代码修 bug 时顺手升级”的节奏。
- 用工具辅助识别非 BEM 类名:运行
grep -r "\.[a-z]\+[A-Z]" src/css/找驼峰式命名;用 VS Code 插件PostCSS Language Support高亮疑似嵌套选择器 - 迁移时保留旧类名做兼容:给
<button class="btn btn--large"></button>同时加button button--large,逐步删掉btn - 禁止在 SCSS 中写
&__item { &__text { ... } }这种嵌套——它生成的 CSS 仍依赖结构,且 IDE 不好跳转到button__text定义处
遇到第三方 UI 库(如 Element Plus)怎么共存
不要试图用 BEM 覆盖它们的类名,那等于和源码对抗。正确做法是把第三方组件当“原子块”封装一层,用 BEM 管理它的容器和组合逻辑。
- 比如用
el-input,就包一层<div class="form-field"><el-input class="form-field__input"></el-input></div>,样式只作用于form-field容器和form-field__input这个 wrapper - 避免直接在
.el-button上加!important,改用form-actions__submit包裹后,通过form-actions__submit .el-button提权(注意:仅限必要场景,优先用库自带 prop 控制) - 如果库提供 CSS 变量(如
--el-color-primary),在 BEM 块里用:root或:host覆盖,比硬写选择器更稳定
为什么 BEM 类名变长了,但打包体积反而小了
因为解耦后,CSS 重复率下降,且支持更激进的 PurgeCSS 清理。关键不在类名长度,而在选择器可预测性——工具能准确判断哪些类真的被用到了。
- 启用
purgecss时,配置extractors必须识别 BEM 模式,例如匹配^[a-zA-Z0-9]+(__[a-zA-Z0-9]+)?(--[a-zA-Z0-9]+)?$,否则button--loading会被误删 - 避免在 JS 里拼接类名:
className={`button ${type === 'save' ? 'button--primary' : 'button--secondary'}`}改成用clsx或classnames库,确保字符串字面量可被静态分析 - Webpack 的
css-minimizer-webpack-plugin默认不压缩类名,如需进一步减小体积,可用postcss-rename映射类名,但务必配合 source map 和测试回归,否则调试成本飙升
最常被忽略的一点:BEM 不是命名规范,是协作契约。当团队有人开始写 user-card__header-title,却在另一个文件里用 user-card-header__title,问题就不是语法错,而是契约失效。这类细节没法靠工具拦截,得靠 Code Review 卡住。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











