bem在协作中“越用越乱”主因是人工失误和工具缺失:漏写__、混淆__与--、scss后代选择器、js硬编码类名等;必须用stylelint+ci校验语法、目录约束block边界、生成函数替代手拼字符串。

靠人工记忆和口头约定,BEM 两周内就会崩;必须用工具链卡死语法结构、用目录约束模块边界、用生成函数消灭手拼字符串。
为什么 BEM 在协作中会“越用越乱”
不是规则本身有问题,而是人会漏写双下划线、混淆 __ 和 --、把修饰符塞进元素名、或者在 SCSS 里偷偷写 .card .title 这种后代选择器。更隐蔽的问题是:有人在 JS 里硬编码 className="user-card__avatar",重构块名时漏改一处,样式就断掉且无报错。
常见错误现象:
-
.header__logo--big—— 元素(__logo)不该直接带状态修饰符,应写成.header__logo.header__logo--big或拆出独立状态类 -
.menu-item__icon——menu-item本该是 Block,却当成了元素,导致命名层级错位 -
.card .card__body—— 后代选择器破坏封装性,DOM 加个 wrapper 就失效,且无法反向定位样式来源
用 stylelint + CI 卡死 BEM 语法
光靠 Code Review 拦不住手滑。stylelint-selector-bem-pattern 是目前最稳的校验方案,它能精准识别:.btn-primary(非 BEM)、.CardTitle(大驼峰)、.user_card(单下划线)、.card__header--large--dark(嵌套修饰符)等所有典型违规。
实操建议:
- 在
.stylelintrc.js中配置{ "styleType": "bem" },不设宽松模式 - CI 流程中强制执行
npx stylelint "**/*.{css,scss}",失败即阻断合并 - 禁止在 Prettier 配置中启用
htmlWhitespaceSensitivity: "strict",避免格式化后破坏class="block__elem block__elem--mod"的空格结构
用目录结构和生成函数锁死 Block 边界
Block 名不是随便起的,它必须对应一个物理目录,且该目录下所有 CSS 类名都只能以这个 Block 名开头。否则,user-card 和 userCard 看似只差一个连字符,但工具链会把它当成两个完全无关的组件,校验失效。
实操建议:
- 新组件必须建目录:
user-card/→ 只允许出现.user-card、.user-card__avatar、.user-card--loading - React 中禁用字符串拼接,改用
clsx+ 常量对象:const block = 'user-card'; const cn = (e, m) => `${block}__${e}${m ? '--' + m : ''}`; - Vue 3 的
setup中用computed封装 BEM 工厂函数,避免每个 class 都手动拼
老项目怎么渐进式落地不翻车
全量重命名等于停工一周,真正可行的是守住新增、收敛旧边界、不推倒重来。
实操建议:
- 所有新组件(如弹窗、筛选栏、用户卡片)100% BEM,目录即 Block 名
- 高频冲突类(如全局
.table)先用 wrapper 包一层:<div class="data-table">…</div>,后续扩展全走data-table__row - 遗留
.nav类不急于改,但新增子项必须写成header-nav__item,逐步收口 - 禁止在 BEM Block 内部写类型选择器,如
.card h3—— 必须写成.card__title
最难的不是记住双下划线,而是每次写新类名前停半秒:它是谁的一部分?它有没有独立存在的意义?改一个 --disabled,会不会意外影响别的 __input?
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











