预加载图片需精准控制时机、缓存与渲染解耦;唯一原生有效方式是<link rel="preload" as="image">置于<head>顶部,配合fetchpriority="high"(Chrome 101+),禁用loading="lazy"混用;运行时按需预加载用new Image(),须先绑定事件再赋值src并检查img.complete;严禁用display:none或loading="lazy"替代预加载。

预加载图片不是“让浏览器多下几张图”那么简单,关键在于控制资源获取时机、缓存命中率和 DOM 渲染解耦。用错方式反而拖慢首屏、浪费带宽、触发错误优先级。
用 link rel="preload" 声明首屏关键图
这是唯一能被浏览器原生识别、不干扰渲染、且语义准确的预加载方式,适用于 banner、logo、登录主图等确定会在首屏立即使用的资源。
-
as="image"必须显式写出,缺了它浏览器会当成普通 fetch,跳过图片专用缓存策略和解码优化 - 必须写在
<head>里,且越靠前越好;动态插入(比如 JS 创建link)无效 -
href值必须是静态字符串,"/assets/${name}.webp"这类模板字面量会直接请求 404 - 路径需基于 HTML 文档位置解析,
./images/hero.jpg在子目录页会 404,应改用/images/hero.jpg - 可加
fetchpriority="high"(Chrome 101+),旧版忽略无副作用,但别和loading="lazy"混用——逻辑冲突
用 new Image() 实现运行时按需预加载
当你需要根据用户行为(hover、滑动轮播)、设备特性(window.devicePixelRatio)、或状态(模块懒加载后)触发预加载时,Image 对象是唯一可控手段。
- 顺序不能错:先绑定
onload/onerror,再赋值src,否则缓存命中时事件同步触发,回调直接跳过 - 必须检查
img.complete:为true说明已缓存,需立刻执行成功逻辑,不能只等 onload - 并发要节制:浏览器通常限制 6~8 个同源图片请求数,轮播图预加载相邻 3 张就够了,别一次
Promise.all([...10张]) - URL 必须完全一致:预加载用
slide-1.jpg?v=2024,后续切换时也得用同一串 URL,否则缓存不命中
别把 loading="lazy" 或 display: none 当预加载用
这是最常踩的坑:loading="lazy" 是推迟加载,Chrome 默认延迟到滚动前 500px 才发请求;display: none 的 <img> 仍会触发下载,但占用 DOM、干扰 Lighthouse 评分、且无法监听失败。
立即学习“前端免费学习笔记(深入)”;
-
loading="eager"只是默认行为,不等于“提前”,它仍走常规渲染流程,没提速效果 -
decoding="async"只影响解码时机(防主线程卡顿),和下载发起时间无关 - CSS background + 负偏移(如
background-position: -9999px -9999px)能进缓存,但资源优先级低、无加载反馈、且无法 fallback - 伪预加载(如先塞
<img src="x.jpg" style="display:none">)会让浏览器误判资源重要性,可能挤掉真正关键的 CSS/JS 请求
真正难的不是写几行代码,而是判断哪张图值得预加载、什么时候该放、缓存是否真被复用。尤其在 WebP + fallback 场景下,link rel="preload" 只能预载一种格式,另一份还得靠 JS 或 <picture> 补位——漏掉这个细节,高分辨率设备上照样白屏几百毫秒。



















