bem 不是靠名字更长防冲突,而是用 block__element--modifier 将归属、层级、状态锁进类名,使冲突变为可查的结构错误;组件应作为独立 block 当且仅当语义完整、可复用、边界明确。

BEM 不是靠“名字更长”防冲突,而是用 block__element--modifier 把归属、层级、状态全锁进类名里——冲突不再随机发生,而变成一眼可查的结构错误。
怎么判断一个组件该不该作为独立 Block?
Block 必须是语义完整、可复用、有明确边界的单元。不是所有容器都自动是 Block,也不是所有子元素都必须挂靠父 Block。
- ✅ 合理:一个
search-form组件内部有search-form__input和search-form__submit,它自己就是 Block - ✅ 合理:
user-card和profile-card是两个独立 Block,各自定义user-card__avatar和profile-card__avatar,互不干扰 - ❌ 错误:把第三方组件(如
ant-select)强行改写成form__ant-select—— 它不是你项目的 Block,硬套会破坏封装和升级路径 - ❌ 错误:写
card__button__icon—— BEM 不支持三级嵌套,button若是独立功能单元,就该是自己的 Block
为什么 __ 和 -- 不能替换成 - 或省略?
这不是风格偏好,是工具链识别 BEM 结构的硬性锚点。漏写、错写会直接导致 lint 失效、VS Code 插件无法高亮、构建时样式提取逻辑出错。
- ❌ 错误写法:
.button__icon_error(用单_代替--)、.card-header--large(漏掉一个_)、.card__body--compact--dark(嵌套修饰符) - ✅ 正确做法:配置
stylelint-selector-bem-pattern,规则设为{"styleType": "bem"},CI 中跑npx stylelint "**/*.{css,scss}"阻断违规提交 - ⚠️ 注意:SCSS 嵌套写法
.card { .title { } }编译后是.card .title,破坏 BEM 封装性,且无法反推来源;必须平铺书写
JS 动态拼接类名时最容易踩什么坑?
手拼字符串看着快,实际在 CI 环境下极易因空格、连字符、大小写漏写而挂掉,本地开发跑得通,Linux 构建直接报错。
- ❌ 危险写法:
className={`button button--${variant} ${hasIcon ? 'button__icon' : ''}`}(缺空格、button__icon脱离 Block、无防错) - ✅ 推荐封装常量:
const BLOCK = 'search-form'; const cn = (e, m) => `${BLOCK}${e ? '__' + e : ''}${m ? '--' + m : ''}`,调用cn('input', 'disabled')得到search-form__input--disabled - ⚠️ 注意:Modifier 名必须表达意图或状态(如
button--loading),而非纯视觉描述(如button--red),否则换主题时无法映射逻辑
什么时候该放弃 BEM,而不是硬套?
BEM 在中大型协作项目里降低沟通成本,但它不是银弹。强行套用反而增加认知负担。
- ✅ 放弃场景:组件粒度极小且无复用(比如一页只有一个
hero-banner),加hero-banner__title反而冗余 - ✅ 放弃场景:使用 CSS-in-JS(如
styled-components),const Title = styled.h1已隐含归属关系,再套 BEM 类名是叠床架屋 - ✅ 放弃场景:团队已稳定使用其他约定(如 SUIT CSS),切换成本远高于收益
- ⚠️ 注意:BEM 的核心价值不在“写得多”,而在“边界清”——一旦 Block 边界模糊(比如把整个页面当一个
pageBlock),规范就失效了
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











