真正有效的防线是机器可读的索引+自动化校验,把命名冲突从运行时Bug变为提交前报错;需建blocks.json映射Block名与业务语义,配合ESLint/stylelint白名单、CI扫描比对、VS Code插件提示及近义词检查,防“同义不同名”。

光靠手写 BEM 类名和口头约定,重复问题不是“会不会发生”,而是“什么时候爆发”。真正有效的防线是机器可读的索引 + 自动化校验,把命名冲突从运行时 Bug 变成提交前报错。
为什么 user-card 和 profile-card 算重复
它们不是字符串重名,而是语义重名——指同一类 UI 单元(用户信息展示卡片),却在不同目录下各自实现、各自加修饰符、样式几乎一样却无法复用。BEM 防不住这种“同义不同名”,只防得住 user-card__avatar 和 user-card__avatar 的字面重复。
- 真正要索引的不是类名本身,而是业务语义:比如
"user-card": "用于个人中心、订单页、客服会话侧边栏等场景的用户基础信息展示" - 设计师说“会员卡片”,前端起
vip-card,后端字段是memberCard,JS 里混用.vip-card__name和.member-card__name,结果查不到元素——这不是拼写错误,是语义断连 - CI 扫描不能只 grep
.user-card,得比对 blocks.json 里是否已存在语义近似的条目,比如profile-card和user-card被近义词库标为强相关,就该告警
怎么建一份能跑进 CI 的 blocks.json 索引表
它必须是纯 JSON、无注释、字段固定,否则脚本解析会失败。内容不是“所有类名列表”,而是 Block 名到业务语义的映射,且每个 Block 名只能出现一次。
- 文件路径必须统一,比如
src/styles/blocks.json,所有工具都硬编码读这里 - 格式严格为:
{"user-card": "用户信息卡片组件", "search-form": "全局搜索表单"},不允许多层嵌套或额外字段 - 新增 Block 必须走 PR,CI 中运行校验脚本:
node scripts/validate-blocks.js,检查是否与现有条目语义重复、命名是否含非法字符(如大写字母、下划线开头) - VS Code 插件(如 BEM Helper)配置指向该文件,输入
user-时只提示已注册的user-card、user-list,不出现userProfile这类未登记名
stylelint-selector-bem-pattern 配置容易漏的关键项
只开插件不等于防护生效。常见失效原因是规则没对齐团队实际用法,或没覆盖所有文件类型。
立即学习“前端免费学习笔记(深入)”;
- 必须显式声明
componentName白名单:"componentName": ["user-card", "search-form"],否则它只校验语法(__/--),不管这个 Block 名是否合法 - SCSS 文件也要纳入 lint 范围:
npx stylelint "**/*.{css,scss,sss}",否则&__input嵌套写法逃过检查 - 禁止后代选择器必须单独配规则:
selector-no-descendant-combinator,因为stylelint-selector-bem-pattern不管这个 - CI 中必须设为
fail而非warn,否则违规类名照样合入,校验形同虚设
动态拼接类名时,classNames 库也救不了的坑
React/Vue 里用 classNames('user-card', { 'user-card--large': isLarge }) 看似安全,但 Block 名硬编码在字符串里,一旦 user-card 在 blocks.json 里被改名或废弃,JS 层完全感知不到。
- 正确做法是每个组件顶部定义常量:
const BLOCK = 'user-card';,然后用classNames(BLOCK, { [`${BLOCK}--large`]: isLarge }) - 更进一步,封装一个
bem()工具函数,自动读取blocks.json校验BLOCK是否合法,本地开发时直接报错 - 禁止在 JSX 中出现任何裸字符串类名:
className="user-card__avatar",必须经过bem()或classNames派生,否则 ESLint 插件no-restricted-syntax直接拦截
最常被忽略的是:索引表和校验规则本身也会过期。blocks.json 里写着 "page-header": "全局页头",但半年后团队已统一改用 layout-header,旧条目没清理,新成员照着抄,冲突照旧发生。索引不是建完就完事,得像代码一样定期 Review 和归档。


















