bem + scoped 是分工协作:bem 管命名意图与模块归属,scoped 管作用域不外溢;block 名必须与组件文件名一致(如 usercard.vue → user-card),否则语义断裂、样式混乱。

BEM + scoped 不是“加个前缀就完事”,而是 Block 命名必须与组件强绑定、scoped 只负责不外溢,两者缺一不可——否则类名还是乱,样式照样崩。
Block 名必须和 Vue 文件名一致,小写+短横线
这是最容易被跳过的硬规则。比如组件叫 UserCard.vue,Block 名就得是 user-card,不是 card、user 或 userCard。一旦偏离,BEM 的模块归属语义就断了:
-
user-card__avatar✅ —— 一眼知道属于哪个组件、哪个逻辑单元 -
__avatar❌ —— 编译报错,SCSS 中 & 嵌套失效 -
avatar❌ —— 和其他组件的avatar冲突,scoped挡不住这种内部混乱 -
UserCard__avatar❌ —— 大驼峰在 CSS 中非法,且破坏 BEM 小写约定
Element 必须用双下划线连接,且不能脱离 Block 独立存在
Element 是 Block 的“零件”,不是通用工具类。user-card__header 合法;但单独写 __header 或 header 就等于放弃 BEM 的结构约束:
- 模板里写
<div class="user-card__header">,样式里必须用 <code>.user-card__header或 SCSS 的&__header,不能漏掉 Block 前缀 - 禁止出现
user-card__header__title—— 这是嵌套 Element,违反 BEM 分层,应改为user-card__header-title或拆成独立 Block -
scoped不会帮你校验这个,全靠命名自觉。一旦漏写前缀,调试时根本分不清这个title是谁家的 -
user-card--compact✅ —— 改变 padding、font-size,有明确上下文(如列表密度切换) -
user-card__avatar--large✅ —— 同一元素不同尺寸变体,可组合使用 -
user-card--default❌ —— 默认态无需修饰符,加了反而让基础样式依赖冗余类 -
user-card__avatar--large--rotated❌ —— 多修饰符连写难维护,应拆成user-card__avatar--large user-card__avatar--rotated - 模板中包一层 BEM 容器:
<div class="user-card__action-btn"><el-button>编辑</el-button></div> - 样式中只穿透该容器下的目标:
.user-card__action-btn ::v-deep(.el-button) { height: 32px; } - 这样既避免全局污染,又让修改意图可追溯:谁要调按钮样式,就找
user-card__action-btn这个上下文,而不是满项目搜el-button - 注意:
::v-deep仅对当前scoped块生效,不会影响其他组件
Modifier 只用于可预测的状态变更,不为“好看”而加
修饰符不是装饰语法糖,而是 CSS 的状态开关。它只在存在明确视觉/行为差异时才引入:
::v-deep 穿透时,必须包裹在 BEM 容器下
想改 el-button 样式?别直接在 scoped 里写 .el-button——它完全匹配不到。正确路径是“容器限定 + 精准穿透”:
真正难的不是写对 user-card__avatar--hover,而是每次加新类名时,都得问一句:它属于哪个 Block?有没有更上层的语义容器?scoped 能拦住样式外溢,拦不住命名失焦——后者只能靠团队对 Block 边界的共识来守住。











