BEM + scoped 需命名意图与作用域边界双保险:Block 名须严格对齐组件文件名(如 UserCard.vue → user-card),Element 用__连接、Modifier 用--连接且不得脱离 Block,动态类名须静态声明,穿透样式须限定于 BEM 容器内。

BEM + scoped 不是“加个前缀就完事”,而是靠命名意图和作用域边界双保险——没按组件名定 Block,或滥用 ::v-deep 穿透,反而会让样式更难维护。
Block 名必须与组件文件名严格对齐
Vue 组件的 Block 名不是随便起的,它直接决定整个 BEM 结构的语义锚点。比如 UserCard.vue 对应的 Block 必须是 user-card,不能简写成 card 或驼峰式 userCard。
- 错误示例:
<div class="card">→ 无法识别归属,多人协作时极易重复定义 - 正确写法:
<div class="user-card">,所有子元素类名都以此为前缀 - SCSS 中用
&嵌套可自动补全前缀:.user-card { &__avatar { width: 40px; } &--compact { padding: 8px; } } - 如果组件名含数字或大写字母(如
PDFViewer.vue),统一转为小写+短横线:pdf-viewer
Element 和 Modifier 必须依附于 Block 存在
BEM 的 __element 和 --modifier 是强依赖结构,脱离 Block 单独使用会破坏语义隔离,也容易让 scoped 失效。
- 禁止写
class="__header"或class="--loading"—— 这些类名没有 Block 上下文,scoped编译后可能变成__header[data-v-xxx],但模板里根本没这个类,等于白写 - Element 必须双下划线连接,且只描述功能角色:
user-card__name✅,user-card-name❌(易被当成独立 Block) - Modifier 只表达状态变更,不重定义基础样式:
user-card--disabled可改opacity和cursor,但不该动border-radius或font-size - 动态类名必须静态声明:
:class="['user-card', { 'user-card--highlighted': isHighlighted }]",不能写成:class="'user-card--' + variant",否则scoped无法识别并注入属性
穿透子组件样式时必须用 BEM 容器限定范围
要用 ::v-deep 或 :deep() 修改子组件样式时,如果直接写 :deep(.btn),等于把全局类名又拉回作用域,失去隔离意义。
Vue 3 组合式 API 与 最佳实践,涵盖代码组织、useXxx 模式、组合式函数设计、Store 集成、响应式优化及功能抽取。
立即学习“前端免费学习笔记(深入)”;
- 错误做法:
:deep(.el-button) { color: red; }→ 影响所有el-button,且违反 BEM 意图 - 推荐写法:
:deep(.user-card__submit-btn .el-button),用父 Block 容器包裹第三方组件 - 更稳妥的是在子组件根元素上加 BEM 类:
<el-button class="user-card__submit-btn">,然后只在父组件 scoped 中写.user-card__submit-btn,避免穿透 - 若必须穿透,优先用
:deep()(Vue 3.2+ 推荐),而非已废弃的/deep/
动态类名未声明会导致 scoped 生效失败
scoped 的编译机制只处理模板中**显式出现**的类名。运行时拼接的字符串类名(如 class="item-" + type)不会被识别,也就不会加上 data-v-xxx 属性。
- 常见翻车点:
<div :class="'user-card__' + part">→part = 'avatar'时渲染出user-card__avatar,但该类名未被scoped收录,样式不生效 - 解决方案:提前枚举所有可能值,用对象语法静态绑定:
:class="{ 'user-card__avatar': part === 'avatar', 'user-card__name': part === 'name' }" - 复杂场景可用 computed 预生成类名对象,但核心原则不变:类名字符串必须在编译期可静态分析
- 别指望
scoped能“猜中”运行时生成的类名——它做不到,也不该做到
真正难的不是写对一个 user-card__avatar,而是坚持让每个类名都自带“身份证”:谁写的、属于哪个模块、表达什么状态。一旦松懈,BEM 就退化成带下划线的普通命名,scoped 也只剩一层脆弱的属性外壳。

















