每个树节点必须是独立 block:tree-node 自身封装展开/缩进/图标逻辑,data-level 控制缩进,图标用 tree-node__icon--expanded,复杂内容升格为 person-card 等独立块。

每个树节点必须是独立 block,不是元素
树形结构天然递归,但 BEM 不允许把整棵树塞进一个 tree 块里再靠 tree__node 无限嵌套。那样会让样式泄漏、复用困难、JS 查询失效。真实项目里最常踩的坑,就是写了个 tree__node,结果发现它依赖父级 tree__children 才能缩进或展开——这已经违背了 BEM 的“块独立”原则。
正确做法是:每个节点自身就是一个 tree-node 块。它的展开/折叠、图标状态、缩进逻辑全部封装在内部,不向外暴露结构依赖。
-
tree-node是块,可单独渲染、测试、复用 -
tree-node__children只是布局容器,不带业务逻辑,也不控制子节点样式 - 子节点是另一个
tree-node实例,不是tree-node__node或tree-node__children__node
缩进不能靠类名层级,得用 data-level + 属性选择器
别写 tree-node--level-2 或 tree-node__children__node 这种纯表现类。BEM 明确反对用类名表达“第几级”,因为树深度一变,HTML 就得批量改,CSS 也得跟着调。
缩进是视觉表现,不是语义层级,应该由数据驱动:
立即学习“前端免费学习笔记(深入)”;
- JS 渲染时给每个
tree-node添加data-level="2"属性 - CSS 用属性选择器控制:
.tree-node[data-level="0"] { padding-left: 0; } - 避免后代选择器:
.tree-node__children .tree-node会随 DOM 深度增加权重,且易被结构变更破坏
图标、展开按钮这些交互部件要作为元素,用修饰符表达状态
图标不是独立组件,而是 tree-node 的一部分,所以命名必须落在块内:用 tree-node__icon,而不是 icon 或 tree-icon。它的状态(展开/收起/禁用)通过修饰符体现,比如 tree-node__icon--expanded。
注意修饰符必须绑定到对应元素,不能越级:
- ✅
tree-node__icon--expanded:图标自身的状态 - ❌
tree-node--expanded__icon:语法错误,BEM 不支持修饰符挂载在元素上 - ⚠️
tree-node--expanded和tree-node__icon组合使用才是正交方案
多层嵌套时,子模块要升格为独立 block,别硬套 __ 嵌套
当树节点里嵌了复杂内容(比如带操作栏的部门卡片、含头像和联系人的人员节点),别继续往 tree-node__content__header__title 里钻。BEM 的 __ 表示“直属子节点”,不是“任意嵌套路径”。出现三层及以上 __,说明结构错了。
真实解法是升格:
- 把人员信息抽成
person-card块,内部用person-card__avatar、person-card__name - 在
tree-node__content里直接放<div class="person-card"></div> - HTML 中保持
tree-node和person-card平级,不嵌套类名
最容易被忽略的一点:BEM 的边界感不是靠命名规则守住的,而是靠每次加新 DOM 节点时,下意识问一句——“它能脱离当前上下文单独存在吗?” 如果答案是否定的,那就该升格为 block。


















