Transfer组件应拆为4个独立Block:transfer(容器布局)、transfer-list(可复用数据列表)、transfer-item(单条数据渲染)、transfer-actions(操作按钮组),各自职责清晰、可独立预览和复用。

Transfer组件该拆成几个Block?
不是“一个Transfer就是一个Block”,而是按可复用边界切分。典型错误是把整个穿梭框写成transfer块,然后塞进transfer__source、transfer__target、transfer__control——这三者其实都能独立存在:源列表可复用为通用数据列表,目标区可能是另一个list,操作按钮组(移入/移出/全选)更常被抽成action-group块。
推荐拆法:
-
transfer:只负责整体容器、布局流、无障碍属性(如role="application"),不包含任何业务逻辑样式 -
transfer-list:独立块,含标题、搜索、滚动容器、项列表;它能在用户管理页、权限配置页单独复用 -
transfer-item:独立块,封装单条数据的渲染(头像+姓名+状态),支持transfer-item--selected等修饰符 -
transfer-actions:独立块,控制按钮排列、禁用态、加载态,不耦合源/目标语义
验证标准:删掉transfer容器后,transfer-list能否在 Storybook 里独立预览并交互?能 → 它就是合法 Block。
元素命名为什么不能出现 transfer__list__header?
transfer__list__header 违反 BEM 单层元素关系,工具链会失效,人眼也难维护。它暴露的问题是:你误把list当成了transfer的内部元素,而实际上transfer-list已是独立 Block。
立即学习“前端免费学习笔记(深入)”;
正确结构:
-
transfer-list__header✅(transfer-list块下的直系元素) -
transfer-list__search✅ -
transfer-list__items✅(容器,不是语义项) -
transfer-item✅(独立块,非transfer-list__item)
如果硬要表达“穿梭框里的列表标题”,就靠 DOM 结构和 CSS 布局实现,而不是靠类名嵌套。BEM 不负责描述位置关系,只负责声明归属关系。
修饰符该加在 transfer 还是 transfer-item 上?
修饰符永远绑定到**发生状态变化的具体 UI 单元**。比如某条数据被禁用不可选,不是整个穿梭框变灰,也不是源列表整体禁用,而是那一条transfer-item进入禁用态 —— 所以必须用transfer-item--disabled,而非transfer--disabled或transfer-list--disabled。
常见错误写法及后果:
-
transfer--disabled .transfer-item:后代选择器破坏作用域隔离,一旦transfer-item被挪到其他上下文,样式就错乱 -
transfer-item--selected--disabled:链式修饰符非法,无法被stylelint-selector-bem-pattern识别,也无法被clsx安全组合 -
transfer-item--disabled✅ 可与transfer-item--selected并列使用,状态正交、可预测
运行时状态(如加载中)建议用is-前缀:transfer-item--is-loading,和静态修饰符--disabled区分语义。
SCSS里怎么写才不产出非法选择器?
SCSS 的 & 是字符串拼接器,不是 BEM 保险丝。一不留神就会编译出transfer--compact__header这种非法名(修饰符不能直接修饰元素)。
安全写法只用 &__ 和 &--,禁用空格嵌套:
.transfer-list {
&__header { ... }
&__search { ... }
&__items { ... }
&--compact {
&__header { padding: 4px; } // ✅ 编译为 .transfer-list--compact__header
}
&__header {
&--sticky { position: sticky; } // ✅ 编译为 .transfer-list__header--sticky
}
}
绝对禁止:
-
&--compact { &__header { ... } }→ 产出.transfer-list--compact__header❌ -
&__header { &--sticky &--large { ... } }→ 产出.transfer-list__header--sticky--large❌
最易被忽略的是:文件路径必须对齐 Block 名。transfer-list.scss 文件里只允许出现以transfer-list开头的类,否则 BEM 就退化成带分隔符的字符串游戏。


















