BEM通过将模块归属写入类名(如user-card__avatar)解决scoped样式无法避免语义冲突的问题,强制类名与组件文件名严格对齐,并禁止裸名、后代选择器和跨块复用以保障隔离性。

因为只靠 无法防止类名语义冲突,而 BEM 把模块归属直接写进类名里——user-card__avatar 一出现,就知道它属于 UserCard.vue,不会和 modal__avatar 撞,也不怕被全局 .avatar 覆盖。
scoped 属性解决不了类名泛化问题
只加 data-v-xxx 属性选择器,不约束你写什么类名。你仍可以写 .header、.body、.btn,结果:
- 多个组件都用
.btn,DevTools 里点开一个按钮,样式来源横跨 5 个文件,根本分不清谁在管 - 父子组件嵌套时,父组件的
.list .item和子组件的.item在 scoped 下依然可能因属性选择器权重或拼写巧合互相干扰 - 抽离子组件复用时,
.item突然和新上下文里的同名词冲突,改一处,崩三页
BEM 类名必须与组件文件名严格对齐
不是“起个差不多的名字”,而是硬性映射:文件名 UserCard.vue → Block 名只能是 user-card,所有类名必须以它开头。
- ✅ 正确:
user-card、user-card__avatar、user-card--compact - ❌ 错误:
card__avatar(丢掉user-)、user-card-component__avatar(缩写/扩展不一致)、__avatar(无 Block 前缀) - 文件名变更即 Block 名变更,否则全局搜索
user-card__就漏掉实际使用点,CI 阶段可用stylelint-selector-bem-pattern校验
动态类名不声明就失效
v-bind:class 里拼出来的类名,如果没在模板中静态出现过,scoped 编译器不会为它生成带 data-v-xxx 的选择器。
从零开始编排 SpringBoot+Vue 全栈项目工作流,适用于新建全栈项目或完整脚手架请求,触发后依次调用 product‑spec‑builder(需求调研)等模块。
- ❌ 危险写法:
:class="['user-card', isCompact ? 'user-card--compact' : '']"——user-card--compact若未静态出现,样式不生效 - ✅ 安全写法:模板中至少写一次
user-card--compact(哪怕只是注释),或统一用cn('user-card', { 'user-card--compact': isCompact }) - 修饰符不能嵌套:
user-card__avatar--large--rotated应拆成user-card__avatar--large和user-card__avatar--rotated,否则组合失效且不可维护
穿透第三方组件必须带 BEM 容器限定
想改 样式?别直接 ::v-deep(.el-button),它会污染所有 el-button。
- ❌ 错误:
→ 全局所有 el-button 都变 - ✅ 正确:模板中用 BEM 容器包裹
,样式写提交 .form-actions__submit :deep(.el-button) { height: 32px; } - 这样改的只是「表单操作区里的提交按钮」,删
form-actions目录,样式自然下线,不留残留
真正容易被忽略的是:BEM 的隔离效果,不取决于你写了几个 __,而取决于是否允许任何一处出现裸名、后代选择器或跨块复用——破防一次,整个作用域契约就松动。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










