解决 Next.js Image 组件加载导致 layout shift 的问题 / Fixing Layout Shift Caused by Next.js Image Loading
<h3 data-start="189" data-end="210">🧩 问题描述 / Problem</h3><p data-start="212" data-end="335">在使用 <code data-start="216" data-end="231">flex flex-col</code> 布局时,我遇到了一个明显的 layout shift(布局跳动)问题:<br data-start="267" data-end="270">
当页面加载 <code data-start="276" data-end="287"><Image /></code> 组件时,初始时容器高度较小,但图片加载完成后高度被撑大,导致整体布局被“顶开”,体验很不流畅。</p>
<p data-start="212" data-end="335">❗️This is a common issue when using <code data-start="375" data-end="386"><Image /></code> inside a <code data-start="396" data-end="411">flex flex-col</code> container — the image loads slowly and pushes surrounding content downward, causing <strong data-start="496" data-end="512">layout shift</strong>.</p>
<h3 data-start="520" data-end="537">🎯 目标 / Goals</h3>
<ul data-start="539" data-end="602">
<li data-start="539" data-end="558">
<p data-start="541" data-end="558">✅ 页面加载前不跳动(防 CLS) cumulative layout shift</p>
</li>
<li data-start="559" data-end="580">
<p data-start="561" data-end="580">✅ 图片自适应(responsive)</p>
</li>
<li data-start="581" data-end="602">
<p data-start="583" data-end="602">✅ 支持骨架屏(Skeleton)显示</p>
</li>
</ul>
<h3 data-start="609" data-end="639">🔍 原因分析 / Why This Happens</h3>
<ul data-start="641" data-end="739">
<li data-start="641" data-end="689">
<p data-start="643" data-end="689"><code data-start="643" data-end="654"><Image /></code> 组件未设定父容器的高度或比例时,浏览器在图片加载完成前无法预估尺寸。</p>
</li>
<li data-start="690" data-end="739">
<p data-start="692" data-end="739"><code data-start="692" data-end="702">flex-col</code> 容器的高度是由内容决定的,因此图片一加载,立刻“撑大”父容器,导致跳动。</p>
</li>
</ul>
<h3 data-start="746" data-end="767">✅ 解决方案 / Solution</h3>
<p data-start="769" data-end="818"><strong data-start="769" data-end="818">关键做法:使用一个 <code data-start="781" data-end="786">div</code> 包裹 <code data-start="790" data-end="801"><Image /></code>,设置固定比例,提前占位空间。</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">div </span><span style="color: rgba(255, 0, 0, 1)">className</span><span style="color: rgba(0, 0, 255, 1)">="relative w-full aspect- overflow-hidden"</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">Image
</span><span style="color: rgba(255, 0, 0, 1)">src</span><span style="color: rgba(0, 0, 255, 1)">={item.image}
</span><span style="color: rgba(255, 0, 0, 1)">alt</span><span style="color: rgba(0, 0, 255, 1)">={item.name}
</span><span style="color: rgba(255, 0, 0, 1)">fill
className</span><span style="color: rgba(0, 0, 255, 1)">="object-contain self-center"</span><span style="color: rgba(255, 0, 0, 1)">
priority
</span><span style="color: rgba(0, 0, 255, 1)">/></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">></span></pre>
</div>
<ul>
<li data-start="1022" data-end="1061">
<p data-start="1024" data-end="1061"><code data-start="1024" data-end="1034">relative</code>: 给父容器定位,配合 <code data-start="1046" data-end="1058">Image fill</code> 使用</p>
</li>
<li data-start="1062" data-end="1093">
<p data-start="1064" data-end="1093"><code data-start="1064" data-end="1078">aspect-</code>: 保持宽高比(这里是正方形)</p>
</li>
<li data-start="1094" data-end="1121">
<p data-start="1096" data-end="1121"><code data-start="1096" data-end="1113">overflow-hidden</code>: 防止内容溢出</p>
</li>
<li data-start="1122" data-end="1167">
<p data-start="1124" data-end="1167"><code data-start="1124" data-end="1152">object-contain self-center</code>: 保证图片不被拉伸,居中显示</p>
</li>
</ul>
<p><strong>🦴 添加骨架屏 / Add Skeleton Placeholder</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">div </span><span style="color: rgba(255, 0, 0, 1)">className</span><span style="color: rgba(0, 0, 255, 1)">="relative w-full aspect- overflow-hidden"</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">Skeleton </span><span style="color: rgba(255, 0, 0, 1)">className</span><span style="color: rgba(0, 0, 255, 1)">="absolute inset-0 w-full h-full"</span> <span style="color: rgba(0, 0, 255, 1)">/></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">></span></pre>
</div>
<p> </p><br><br>
来源:https://www.cnblogs.com/sabertobih/p/18934400
頁:
[1]