bem 在 vue3 中核心是“定边界”而非套规则:block 必须对应独立 .vue 文件,如 documentitem.vue;modifier 仅用于有差异的状态;应通过 scss mixin 或 typescript 工具生成类名,避免手写错误。

直接说结论:BEM 在 Vue3 里不是“套规则”,而是“定边界”——Block 必须对应一个独立的 .vue 文件,否则类名再规范也白搭。
Block 必须是单文件组件(.vue),不能是 template 片段
很多人把 document-listing 当作 Block,然后在内部手写 document-listing__item、document-listing__header,这看似符合 BEM 语法,但违背了 Block 的本质:可复用、可脱离上下文存在。
- ✅ 正确做法:
DocumentItem.vue是独立 Block,类名用document-item,内部元素写document-item__image - ❌ 错误做法:在
DocumentListing.vue模板里写<div class="document-listing__item"> —— 这个 <code>__item实际上是另一个 Block,强行压进父级命名空间,导致无法跨页面复用 - ⚠️ 坑点:
<style scoped></style>不解决语义问题,它只加属性选择器,类名仍可能叫title或box,调试时根本分不清哪个title来自哪个组件 - ✅ 合理:
document-item--highlighted(当前选中)、document-item--compact(密度切换) - ❌ 冗余:
document-item--default、document-item--normal—— 默认样式本就属于document-item,加修饰符反而让所有基础样式都得带两套类 - ⚠️ 坑点:嵌套修饰符如
document-item__icon--large--rotated违反 BEM 组合原则,应拆成document-item__icon--large和document-item__icon--rotated,便于状态叠加与覆盖 - SCSS 方案:定义
@mixin b($block)+@mixin e($elem)+@mixin m($mod),配合@at-root输出扁平类名 - TypeScript 方案:封装
createNamespace('document-item'),返回对象如{ b: 'document-item', e: (e) => `document-item__${e}`, m: (m) => `document-item--${m}` } - ⚠️ 坑点:如果项目用了
useNamespace(如 Element Plus 风格),注意它默认不支持element + modifier组合(即document-item__size--small),需额外扩展逻辑,否则只能手动拼接
Modifier 只用于有视觉/行为差异的状态,不是装饰性标签
修饰符不是为了“看起来专业”,而是为 CSS 提供明确的钩子。没有差异,就不该加 --。
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
用 SCSS mixin 或 TypeScript 工具函数生成类名,别手敲
手写 document-item__size--small 容易漏下划线、错拼 --,尤其在频繁迭代时。
BEM 最容易被忽略的复杂点在于:它要求你提前判断组件的复用粒度——这个 Item 是会出现在搜索页、回收站、共享链接三个地方,还是只属于当前列表?一旦判断错了,后面所有类名、样式隔离、甚至组件拆分都会走偏。










