Vue骨架屏在图片加载后为什么会闪一下消失?
我在做商品列表页首屏优化时用了骨架屏,但发现图片加载完成后骨架屏会闪一下才被替换。我用了v-if/v-else控制切换,骨架屏和真实图片的宽高比例也保持一致了,但问题还是存在:
<div v-for="item in items" :key="item.id">
<div v-if="item.loaded" class="product-image">
<img @load="item.loaded = true" :src="item.url" />
</div>
<div v-else class="skeleton">骨架屏内容</div>
</div>
尝试过给img加loading=”lazy”和设置width/height固定值,但切换时仍然有0.5秒的闪烁。这种情况下应该怎样优化过渡效果?是不是骨架屏的结构需要更贴近真实内容布局?
你应该用 display: none 这类样式控制来过渡,而不是用 v-if。具体做法是让骨架和真实内容共存于同一个容器,通过 class 控制显隐。
你可以监听图片的 onload 事件,在回调里设置 loaded 状态,然后用 CSS 做 fade out 效果。关键是不要用 v-if/v-else 拆成两个 div,那样注定会闪。
改法很简单:
然后加点样式过渡:
JS 部分这样处理:
核心就是别用 v-if,用 opacity + transition 做视觉切换。骨架屏的结构确实也要尽量贴近真实布局,但前提是 DOM 不能被干掉。你之前写的 v-if 在图片 load 触发前就把 img 节点删了,等于是先空一帧再塞回去,不闪才怪。
顺便提一句,这种场景其实更适合用 IntersectionObserver 预加载,但那是另一个优化点了。先把切换动画稳住再说。
性能上更合理的做法是使用v-show替代v-if/v-else结构。v-show通过CSS的display属性控制显示隐藏,不会销毁和重建DOM,切换更平滑。你可以把逻辑改成这样:
另外,为了进一步优化视觉体验,可以给骨架屏加一个淡出效果,让切换更柔和:
这样在骨架屏隐藏的时候,会有一个0.3秒的渐隐动画,减少突兀感。同时建议给img设置固定的宽高,防止图片加载后布局抖动:
如果骨架屏内容比较复杂,结构上建议尽量贴近真实布局,特别是文字的位置和样式。这样即使图片加载慢一点,用户也能看到接近真实内容的占位,提升预期感。性能上,这种结构也能减少重绘重排的次数。