骨架屏应使用宿主块的修饰符(如card--loading)而非独立skeleton块名,骨架元素用card__skeleton等子元素名并配合display切换,确保语义准确、样式复用与ssr一致性。

骨架屏元素的BEM命名要匹配真实DOM结构
不能因为“这是骨架屏”就单独搞一套命名逻辑。BEM的核心是语义化组件,skeleton只是card、list-item或article在加载时的视觉状态,不是独立组件。
常见错误是写成 card__skeleton 或 skeleton__card —— 前者把状态当成了子元素(实际它替代了内容),后者虚构了一个不存在的顶层块。
- 正确做法:在原有块内用修饰符表达加载态,比如
card--loading - 骨架元素本身用
card__skeleton作为临时占位元素名,但必须和card__content互斥显示(CSSdisplay: none切换) - 如果骨架结构复杂(如带多段文字+头像+按钮),可用更细粒度修饰符:
card__title--skeleton、card__avatar--skeleton
为什么不用 skeleton 作为块名
因为骨架屏没有独立业务含义,它不承载数据、不响应交互、不参与路由或状态管理——它只是UI的临时投影。一旦真实内容渲染,整个骨架结构通常被移除或隐藏。
用 skeleton 当块名会导致:
- 样式无法复用:不同卡片的骨架高度、圆角、间距各不相同,强行抽成通用
skeleton块反而要大量覆盖样式 - 维护断裂:改了
card的布局,忘了同步更新skeleton的结构,导致骨架和真实内容错位 - SSR/CSR 不一致:服务端没渲染骨架(只吐真实HTML),客户端JS才插入,BEM类名不匹配引发FOUC或样式丢失
--loading 修饰符比 --skeleton 更准确
加载态(--loading)是行为描述,骨架(--skeleton)是实现方式。你今天用灰色矩形,明天可能换成脉冲动画或模糊占位图,但语义仍是“正在加载”。
- 推荐用
card--loading控制整体容器状态,再由它触发内部骨架显示:.card--loading .card__content { display: none; } .card--loading .card__skeleton { display: block; } - 避免
card__skeleton--visible这类冗余修饰符,状态已由父级--loading表达 - 如果需要区分骨架类型(如“简版”“完整版”),用
card--loading--compact而非card--skeleton-compact
React/Vue中动态类名的常见坑
框架里拼接BEM类名容易出错,尤其嵌套修饰符和条件逻辑混在一起时。
- 别写
className={`card ${isLoading ? 'card--loading' : ''}`}—— 多个条件叠加后难以维护 - 用工具库如
clsx:className={clsx('card', { 'card--loading': isLoading })} - 骨架元素不要用
v-if/{isLoading && <div classname="card__skeleton">} 直接控制渲染,否则SSR首屏缺失该节点,hydration 时可能报错或样式错乱;优先用 <code>display: none切换 - 注意伪元素生成的骨架(如
::before+content)无法被无障碍阅读器忽略,真需要语义隔离时,得加aria-hidden="true"
BEM不是贴标签比赛,关键在让后续维护的人一眼看懂“这个类为什么存在、它依附于谁、什么条件下生效”。骨架屏类名一旦脱离宿主块的上下文,就失去了BEM存在的意义。











