骨架屏不应淡出,而应被真实内容自然替换;正确做法是让内容淡入,通过条件渲染控制DOM存在,CSS仅负责内容入场动画,避免重叠、CLS和布局偏移。

transition 不能直接控制骨架屏淡出
骨架屏不是靠 opacity 切换显隐的 UI 元素,而是和真实内容共存、渐进替换的占位结构。强行给骨架容器加 transition: opacity,会导致真实内容渲染时闪一下、错位或延迟消失——因为骨架屏本就不该“淡出”,而应被内容自然顶替。
真正要过渡的是 content 的入场,不是 skeleton 的退场
生产环境里,骨架屏存在期间,真实内容通常已准备就绪,只是尚未挂载或未触发渲染。此时该做的是让真实内容淡入,而非让骨架淡出。关键点在于:
-
SkeletonCard组件内部用opacity: 0+transition预留真实内容位置,等数据就绪后先设opacity: 0渲染 DOM,再加类触发opacity: 1 - 必须配
visibility: hidden+transition: visibility 0.3s step-end,避免opacity: 0时仍可交互 - 骨架容器本身保持
display: block,不设transition;它的“消失”是 DOM 替换或条件渲染的结果,不是 CSS 动画
容易踩的坑:把 skeleton 当 modal 处理
有人照搬弹窗写法,在骨架层上写 .skeleton { opacity: 1; transition: opacity 0.3s; },再通过 JS 切换 class。这会引发三个问题:
- 骨架和真实内容重叠渲染,造成文字发虚、iOS Safari 锯齿
- 加载完成瞬间两个结构同时存在,CLS(累计布局偏移)飙升
- 移动端反复切换 tab 时,
opacity过渡卡顿,尤其在低端安卓机上
正确路径是:骨架只负责占位与光效,内容一到立刻替换节点或切换 loading 状态,靠 React/Vue 的条件渲染控制 DOM 存在与否,CSS 只管内容入场动画。
立即学习“前端免费学习笔记(深入)”;
如果真要加淡出效果,只限局部替换场景
极少数情况(如列表项逐条加载),需要某一条骨架“淡出”、对应真实卡片“淡入”。这时应:
- 对单个卡片级容器设
transition: opacity 0.2s ease-out - 真实内容挂载后,先设
opacity: 0,再setTimeout(() => setOpacity(1), 16)触发重绘 - 绝对不要用
display: none或visibility: hidden控制骨架,否则 layout shift 不可避免 - 确保骨架和真实卡片使用完全一致的
height、padding、border-radius,否则淡入时边缘会跳
最常被忽略的是:骨架屏的“退出”从来不是视觉动效问题,而是 DOM 生命周期管理问题。光调 transition 参数,不如先确认真实内容是否在骨架卸载前已完成尺寸计算和样式继承。


















