bem 的扩展性源于三处刚性约束:修饰符必须锚定具体 block 或 element、block 必须语义自洽可移植、动态类名必须由工具受控;修饰符仅作变量开关,不直接写样式。

修饰符必须绑定到具体 Block 或 Element,不能泛化使用
BEM 的修饰符不是装饰性后缀,而是语义锚点。写 .button--disabled 看似省事,但一旦按钮被复用在表单提交、弹窗确认、侧边栏操作三个场景,这个修饰符就失去判断依据:它禁用的是点击?键盘焦点?还是整个组件生命周期?
常见错误现象:.card--blue、.input--big 这类修饰符无法扩展——“blue”是品牌色还是状态色?“big”相对谁而言?后续加 --small 就自相矛盾。
- 始终让修饰符依附于明确实体:
.button__trigger--disabled(触发区域不可交互)、.card__header--featured(标题区域高亮) - 布尔型修饰符(如
--disabled)只表达“有/无”,不带值;键值型(如--size-large)必须用语义化单词,避免数字或技术术语(--radius-4px❌) - 初始设计就要预判变体是否可枚举:圆角未来要支持
sm/lg/full,就直接定义为--radius-sm,而非先写--rounded再打补丁
用 CSS 自定义属性承接修饰符逻辑,别在类里硬编码样式
纯 BEM 类名只能做分支开关,真正提升扩展性的关键是把修饰符变成变量注入点。比如换主题时,.button--theme-dark 不该直接写 background: #222,而应设变量再复用。
使用场景:同一套组件需适配高对比度模式、移动端紧凑间距、RTL 布局翻转。
- 在 Block 根节点声明变量:
.button--theme-dark { --button-bg: #222; --button-color: #fff; } - 元素内部统一用
var(--button-bg),避免重复写死值 - 禁止在修饰符类中塞完整样式规则——否则换主题就得全局搜索替换,而不是改一处变量
Block 命名必须抽象,Element 层级严格限制为一层
扩展性差的根源常在于 Block 命名过度耦合业务或位置。.homepage-hero-banner 无法复用于产品页,.sidebar-user-card 搬到弹窗里就失效。BEM 要求 Block 是语义自洽的功能单元,能脱离上下文存在。
常见错误现象:DOM 结构一动,CSS 全崩;搜索 user-card 找不到所有相关样式,因为有的叫 profile-card,有的叫 member-card。
- Block 名小写+短横线:
hero-banner✅,HeroBanner❌ - Element 只允许单层嵌套:
search-form__input✅,search-form__header__title❌(后者应拆成search-form__header+search-form__header-title或直接提升为独立 Block) - Element 必须依附于 Block 存在:移除
card后,card__avatar不再有意义;但avatar本身若跨场景复用一致,则它自己就是 Block
动态类名必须用工具管理,手拼字符串极易出错
JS 中用字符串拼接类名是 BEM 最脆弱的一环。el.className = 'button' + (isPrimary ? '--primary' : '') 容易漏掉空格、下划线数量错乱,或把 -- 和 __ 混用。
性能影响常被忽略:每个列表项都动态加 list-item--first list-item--odd list-item--visible,DOM size 和 style recalc 会明显上涨。
- 用
classnames库管理:cx('button', { 'button--primary': isPrimary, 'button__icon--hidden': isIconHidden }) - JS 操作类名必须严格匹配 BEM 字符串:
el.classList.add('card__body--expanded'),别用模板字符串拼接 - Vite/Webpack 项目推荐配
postcss-bem插件,支持@block语法自动注入前缀,防手误
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











