product-list 是合法 Block,因能跨页复用、独立渲染与交互;homepage-product-list 等含页面语义的则不是,因其绑定上下文导致复用即错乱。

商品列表本身是不是 Block?先看它能不能跨页复用
直接说结论:product-list 是合法 Block,但 homepage-product-list 或 search-result-list 不是。前者能用在首页、搜索页、分类页、弹窗推荐位;后者一写就锁死上下文,后续任何复用尝试都会触发样式错乱或 JS 选择器失效。
判断标准就一条:这个 UI 单元是否脱离当前页面后,仍能独立渲染、交互、埋点?能,就是 Block;不能,说明它还没切到合理边界。
-
product-list合格:结构稳定(标题+网格+分页)、数据源抽象(支持传入任意商品数组)、无隐含 DOM 依赖(不假设父级有.main-content) -
category-product-list不合格:“category” 是业务位置,不是功能语义;一旦用在搜索页,类名就失焦,Sass 变量映射和 CI 校验全失效 - 命名必须小写 + 中划线:
productlist或ProductList都不行——grep 查不到、VS Code 插件不补全、stylelint-selector-bem-pattern 直接报错
列表里的卡片、分页、加载态怎么拆 Element?别嵌套三层
product-list__item 是对的,product-list__item__image 就越界了。BEM 明确禁止元素再带子元素,因为这等于把 DOM 层级硬编码进类名,破坏可移植性。
常见错误是顺着 HTML 结构一路往下写:<div class="product-list"><div class="product-list__item"><div class="product-list__item__image">...</div></div></div>。这种写法会让工具链拒绝编译,CI 构建失败,且无法支持卡片在邮件模板中单独渲染。
立即学习“前端免费学习笔记(深入)”;
- 所有一级子节点都应是
product-list的直接 Element:product-list__title、product-list__grid、product-list__pagination、product-list__loading -
product-list__grid内部若需控制列数,用修饰符:product-list__grid--cols-2、product-list__grid--cols-4,而不是新增product-list__grid__col - 如果“图片下方的价格标签”总和图片强耦合,且在多个列表中重复出现,它不该是
product-list__item__price,而应抽成独立 Block:price-tag,然后在 item 里用product-list__item__price-tag
Modifier 怎么写才不会后期全量替换?避开颜色/尺寸类名
product-list--compact 安全,product-list--red 危险。电商项目换主题色是常态,一旦用了 --red,下次改品牌色就得全局搜改,还容易漏掉 JS 动态拼接的类名(比如 el.classList.add('product-list--red'))。
Modifier 必须表达可预测的状态或语义,而非视觉结果。它要能回答“这个列表现在处于什么逻辑状态?”而不是“它现在看起来是什么颜色?”
- 推荐写法:
product-list--loading、product-list--empty、product-list--infinite-scroll、product-list--with-filters - 响应式断点下沉到 Modifier 层:
product-list__grid--mobile-2而非媒体查询里写.product-list__grid { @media (max-width: 768px) { grid-template-columns: repeat(2, 1fr); } }——这样便于统一开关、支持服务端渲染条件注入 - 慎用
is-类:如is-hidden是通用状态,不属于 BEM 范畴;BEM 的 Modifier 必须绑定到具体 Block,product-list--hidden才合规
HTML 里要不要显式写全类名?JS 拿不到节点就白搭
只改 CSS 类名、不改 HTML 是最常见返工原因。比如把旧代码 <div class="product-list"><div class="item">...</div></div> 改成 .product-list__item,但 HTML 里还是 class="item",那所有 JS 查询 document.querySelectorAll('.product-list__item') 都为空。
BEM 的作用域隔离完全依赖类名本身,不依赖 DOM 层级。这意味着每个元素必须在 HTML 中显式声明完整 BEM 类名,且只含一个 Block 相关类。
- 正确 HTML:
<div class="product-list"><div class="product-list__item"><div class="product-list__item__image">❌ 错误,__item__image违反语法 - 正确 HTML:
<div class="product-list"><div class="product-list__item"><div class="product-list__image">✅ 元素平级,无嵌套 - JS 里不要用
querySelector('.item'),必须用querySelector('.product-list__item')——否则在详情页插入同结构卡片时,会误取到无关节点 - 组件化场景下(React/Vue),Block 名建议定义为常量:
const BLOCK = 'product-list';,所有类名基于它派生,避免拼写不一致


















