最简可行方案是用new Image()创建实例并设置src,浏览器立即发起请求并缓存;必须显式监听onload和onerror,确保路径准确、避免重复创建,且需注意移动端可见性及并发控制。

图片预加载最简可行方案:用 Image 构造函数
直接 new 一个 Image 实例,设置 src,浏览器就会发起请求并缓存——这是所有预加载的底层基础,不依赖任何框架或 API。
常见错误是设了 src 却没监听 load 或 error,导致失败时完全无感知;更隐蔽的问题是把预加载逻辑写在 DOM ready 之后,但用户已经滚动到图片位置,白屏一闪就过去了。
- 必须显式监听
onload和onerror,哪怕只做空处理,否则无法判断是否就绪 - 路径要确保准确:相对路径以当前 HTML 为基准,不是 JS 文件所在目录
- 避免重复创建同一 URL 的
Image实例,浏览器虽会复用缓存,但事件监听器不会自动共享 - 移动端要注意,部分 iOS WebView 在页面不可见时会暂停图片加载,可配合
visibilitychange事件补发
const img = new Image();
img.onload = () => console.log('预加载完成');
img.onerror = () => console.error('预加载失败');
img.src = '/assets/banner.jpg'; // 立即触发请求
批量预加载时如何控制并发和顺序
一次性 new Image() 十几张图,可能触发浏览器并发连接数限制(通常 6~8 个),导致后置图片排队严重,实际加载时间反而拉长。
不用第三方库也能简单控流:用队列 + 递归调用,每次只放行固定数量(比如 3 个)同时加载,任一完成就补位下一个。
-
fetch()+createObjectURL()不适合纯图片预加载,它绕过浏览器图片解码缓存,后续<img src>仍需重新下载 - 不要用
Promise.all()包裹全部Image加载,失败一个就全盘中断,且无法控并发 - 优先级很重要:首屏图应立即触发,非首屏可延迟 300ms 再进队列,避免阻塞关键资源
function preloadImages(urls, maxConcurrent = 3) {
const queue = [...urls];
let activeCount = 0;
function loadNext() {
if (queue.length === 0 && activeCount === 0) return;
if (activeCount >= maxConcurrent || queue.length === 0) return;
const url = queue.shift();
activeCount++;
const img = new Image();
img.onload = img.onerror = () => {
activeCount--;
loadNext();
};
img.src = url;
}
while (activeCount < maxConcurrent && queue.length > 0) {
loadNext();
}
}
预加载后如何让真实 <img> 标签立刻显示
很多人以为预加载完,DOM 里再写 <img src="xxx"> 就能秒出——但现实是,如果此时该 img 元素还没插入文档流,或者 CSS 设置了 opacity: 0 / display: none,浏览器根本不会触发渲染管线,缓存再好也白搭。
真正起效的前提只有一个:元素已挂载、CSS 可见性允许绘制、且 src 值与预加载时完全一致(包括大小写、查询参数)。
- URL 必须严格相等:
/a.jpg和/a.JPG在某些服务器上是不同资源 - 带时间戳参数的 URL(如
?t=123)每次都不一样,预加载无效——改用 HTTP 缓存头控制,而非靠参数骗缓存 - 服务端开启
Cache-Control: public, max-age=31536000比前端预加载更可靠,预加载只是补位手段 - React/Vue 等框架中,避免在组件
useEffect或mounted钩子中才设src,最好在数据获取阶段就启动预加载
Service Worker 场景下预加载的特殊行为
如果站点已注册 Service Worker,new Image().src 的请求默认走 SW 路由。这意味着:预加载是否生效,取决于 SW 的 fetch 事件里有没有命中缓存策略。
容易被忽略的是,SW 缓存和浏览器 HTTP 缓存是两套机制。即使你配置了强缓存头,若 SW 的 fetch 事件没主动读 cache,图片依然会重新请求网络。
- 在 SW 中对图片资源做
cache.match()并event.respondWith()返回缓存响应,才能让预加载真正“落地” - 不要在 SW 里对预加载 URL 做重定向或改写,会导致
Image实例的src和最终缓存 key 不一致 - 调试时注意 DevTools 的 Network 面板过滤
Img类型,勾选 “Disable cache” 会绕过所有缓存(包括预加载结果)

















