图片预加载是确保资源提前进入缓存而非提前显示,正确方式是用<link rel="preload" as="image">预载首屏关键图,或new Image()实现条件预加载,二者分工协作。

图片预加载不是“让提前显示”,而是确保资源在真正需要前已进入浏览器缓存——做错方向(比如滥用
loading="lazy")反而拖慢首屏。
用 <link rel="preload"> 声明首屏关键图
这是最轻量、最可控的预加载方式,适用于 banner、logo、登录页主视觉等确定会立即使用的图。
-
as="image"必须写,缺了浏览器会当普通 fetch 处理,无法启用图片专用缓存和解码策略 - 必须放在
<head>里,且越早越好;动态插入或 JS 注入无效 -
href必须是静态路径,href="/assets/${name}.webp"这类模板字面量会直接 404 - 相对路径以 HTML 文档位置为基准,
./images/hero.jpg在/user/profile.html中会请求/user/images/hero.jpg,不是你想要的 -
fetchpriority="high"可选,Chrome 101+ 有效,旧版忽略无副作用
<head> <link rel="preload" as="image" href="/assets/logo.webp" fetchpriority="high"> <link rel="preload" as="image" href="/assets/banner.jpg"> </head>
用 new Image() 实现运行时条件预加载
适合按用户行为、设备像素比、模块可见性等逻辑触发的场景,比如 hover 后预载下一页缩略图,或根据 window.devicePixelRatio 加载 @2x 版本。
- 必须先绑定
onload/onerror,再赋值src;否则缓存命中时事件同步触发,回调直接被跳过 - 要检查
img.complete:为true说明已缓存,需立刻执行成功逻辑 - 避免在循环中密集
new Image()—— 浏览器并发请求数通常限 6~8 个,建议用 Promise 控制并发(如每次最多 4 个) - 不要用
new Image().src = url简写,它无法捕获失败,也无法链式等待
const preloadImage = (url) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = () => reject(new Error(`Failed to load ${url}`));
img.src = url;
});
};
别把 loading="lazy" 或 display: none 当预加载用
这是最常见的误操作。加了 loading="lazy" 的首屏图,Chrome 仍可能等到滚动前 500px 才发请求——完全违背预加载意图。
-
loading="eager"是默认行为,显式写出来只是“不懒”,不等于“提前” -
decoding="async"只影响解码时机(防主线程卡顿),和下载时机无关 -
display: none+<img>或 CSS background 伪隐藏,会参与初始渲染流程,增加首屏时间,且无法监听状态 - WebP 预加载必须配 fallback:
<link rel="preload">不支持 type 检测,得靠后续<picture>或 JS 判断格式支持情况
真正难的不是写几行代码,而是判断哪张图该预加载、什么时候该停——首屏只预载 2~3 张最核心的图,其余交给懒加载或按需触发;<link rel="preload"> 和 new Image() 不是互斥方案,而是分工:前者保底线,后者控节奏。

















