BEM 是协作底线而非更规范,通过 block__element--modifier 命名锁死归属、角色与状态,杜绝全局样式冲突;需用 stylelint 强制校验、渐进落地、语义约束。

直接写 .btn 或 .header 必然导致样式冲突,这不是风格问题,是作用域失控——CSS 全局生效,谁后加载、谁权重高,谁就赢,跟业务逻辑毫无关系。
为什么 BEM 不是“更规范”,而是协作底线
三人以上维护一个项目两周后,.btn-primary、.button--active、.ui-btn-lg 会同时存在于审查元素里,没人能说清哪个该删、哪个在用。BEM 把归属(user-card)、角色(__avatar)、状态(--large)全锁进类名,人眼可读、机器可查、搜索可定位。
-
.header__logo--highlighted一眼可知:属于 header 块、是 logo 元素、当前为高亮状态 -
.card-title或.title-card这类模糊命名,必须翻 HTML 才能确认上下文 - 修饰符若写成
.header__logo--big,违反 BEM 原则——元素不能带修饰符,应是.header__logo+.header__logo--big并列使用
如何用 stylelint-selector-bem-pattern 卡死新增入口
靠文档和 Code Review 拦不住手滑。必须让违规类名在保存时就报错,否则新代码每天都在污染边界。
- 安装插件:
npm install --save-dev stylelint-selector-bem-pattern - 在
.stylelintrc.js中配置:module.exports = { plugins: ['stylelint-selector-bem-pattern'], rules: { 'plugin/selector-bem-pattern': { componentName: '[a-z][a-zA-Z0-9]+', styleType: 'bem' } } }; - 它会立刻拦截:
.header .logo(结构选择器)、.btn-primary(非 BEM 格式)、CardTitle(大驼峰)、_input(下划线开头) - CI 流程中加
npx stylelint "**/*.{css,scss}",失败即阻断合并
老项目怎么落地,又不让开发卡住
全量重命名等于停工一周,真正可行的是守住新增、收敛旧边界、不推倒重来。
立即学习“前端免费学习笔记(深入)”;
- 所有新组件(如弹窗、筛选栏、用户卡片)100% BEM,目录即 block 名:
modal/下只允许出现modal、modal__header、modal--fullscreen - 高频冲突类(如全局
.table)先用 wrapper 包一层:<div class="data-table">…</div>,后续扩展全走data-table__row等 - 遗留
.nav类不急于改,但新增子项必须写成header-nav__item,逐步收口 - 禁止在 BEM block 内部写类型选择器,如
.card h3—— 这破坏封装性,必须写成.card__title
React/Vue 中怎么避免 BEM 写崩或漏写
纯手写 className="user-card user-card__title user-card__title--large" 极易漏掉双下划线、拼错 modifier,也难和 JS 逻辑对齐。
- React 推荐用
clsx+ 常量对象:const b = 'user-card'; const cn = (e, m) => `${b}__${e}${m ? '--' + m : ''}`; // 使用:<code>{cn('avatar', 'large')}</code> - Vue 3 的
setup中可用computed封装 BEM 工厂函数,避免每个 class 都拼字符串 - 禁用 CSS-in-JS 库(如 styled-components)的动态插值生成类名,它绕过 BEM 约束,审查时看到
sc-a1b2c3这类不可读名 - 不要写
className={`card__body ${isCollapsed ? 'card__body--collapsed' : ''}`},改用clsx('card__body', { 'card__body--collapsed': isCollapsed })
最常被忽略的不是语法,而是语义责任:block 名必须是独立可复用的业务单元(如 search-form),不能是 left-nav;element 只能一级嵌套(menu__item__icon 是错的);modifier 只表达外观变体,一旦它触发了网络请求或 DOM 操作,说明逻辑已越界,该抽到 JS 层。


















