骨架屏加载时为什么会出现布局跳动?
我在用 Vue 做商品列表页,加了骨架屏之后,数据加载完成瞬间页面会“闪一下”或者元素位置突然偏移,感觉体验更差了。明明骨架屏和真实内容结构是一样的啊。
我试过给图片容器固定宽高,文字也用了 height: 16px 这种写死高度,但还是跳。是不是哪里没对齐?比如这个骨架屏组件:
<div class="skeleton-card">
<div class="skeleton-img"></div>
<div class="skeleton-text short"></div>
<div class="skeleton-text long"></div>
</div>
而真实内容也是同样结构,但一替换就跳,烦死了……
比如这样:
记得给两个内部div设置完全一样的样式,连margin/padding都要一致。懒得调就复制粘贴样式名完事。
骨架屏和真实内容结构一样,但字体还没加载完、图片没占位、浏览器重排重绘一来,布局就崩了。尤其是中文文字,不同字体渲染高度差挺多的,哪怕你写死
height: 16px,字体 ascent/descent 一变,行高实际占用空间就变了,页面自然跳。我建议你这么搞:
第一,骨架屏容器一定要提前占好位,但别用
height死死卡死,改用min-height+padding或者aspect-ratio来控制比例,比如图片区域:.skeleton-img { aspect-ratio: 1 / 1; background: #eee; }这样哪怕字体变了,图片区域的尺寸不会乱动。
第二,骨架屏的字体用和真实内容一模一样的 font-family、font-size、line-height,甚至可以提前引入字体(比如用
font-display: swap),别让浏览器用 fallback 字体渲染骨架屏,再用主字体渲染内容——这俩一换,高度差直接把下面元素顶飞。第三,关键点:真实内容插入时别用
v-if直接替换整个块,改成用v-show隐藏骨架屏 + 内容渐显,或者用transition包一层,避免 DOM 直接替换触发重排。如果项目里用的是 Element Plus 或 Ant Design Vue,自带的 Skeleton 组件其实已经处理得比较稳了,插件可以直接用,自己手写反而容易踩坑。
最后提醒一句:别在骨架屏里用 flex 布局嵌套太多层,flex 计算高度的时候浏览器经常“算着算着就忘了”,一换内容就崩。尽量用 block + 固定高度的组合,简单粗暴但最稳。