<link rel="preload">是唯一能真正提前触发下载的方式,需在<head>中<meta charset>后、<title>前声明,as="image"不可省略,路径须准确且不支持模板变量;new Image()适用于运行时条件预加载,需先绑定事件再赋值src,并注意并发控制;loading="lazy"、display:none等无法实现预加载。

用 <link rel="preload"> 声明静态关键图
这是唯一能真正“提前触发下载”的方式,适用于你明确知道某张图会在首屏或用户进入后立刻展示的场景(比如 banner、logo、登录页主视觉)。它不渲染、不占布局,只进缓存。
必须写在 <head> 里,且位置要靠前:<meta charset> 后、<title> 前。动态插入(比如 JS 创建)完全无效。
-
as="image"缺一不可——写成as="img"或漏掉,浏览器就当普通 fetch,Priority 降为 Low,Network 面板里 Initiator 显示 (Other) - 路径必须准确:相对路径基于 HTML 文件位置解析,
href="./images/hero.jpg"在/user/profile.html下会请求/user/images/hero.jpg,通常应改用绝对路径/images/hero.jpg - 不支持变量或模板语法:
href="/assets/${name}.webp"是字面量请求,404 铁定发生 - 如果图片是 WebP 格式,记得配 fallback:
<picture>+<source type="image/webp">+<img src="fallback.jpg">,preload 只管 WebP 那条
示例:
<link rel="preload" as="image" href="/assets/hero.webp" fetchpriority="high"> <link rel="preload" as="image" href="/assets/logo.png">
用 new Image() 实现运行时条件预加载
当你需要根据用户行为、设备特性或状态决定是否加载时(比如 hover 后预载下一页缩略图、按 window.devicePixelRatio 加载 @2x 版本),Image 对象是唯一可控的选择。
立即学习“前端免费学习笔记(深入)”;
- 顺序不能错:先绑定
onload/onerror,再赋值src;否则缓存命中时事件同步触发,回调直接跳过 - 检查
img.complete:为true表示已缓存,需立刻执行成功逻辑 - 避免密集创建:
new Image()在循环里狂 new,会撞上浏览器并发限制(通常 6~8 个),建议加 Promise 队列控制并发数(例如每次最多 4 个) - 不用插入 DOM,也不用设
display: none——src赋值即触发下载
基础封装示例:
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" 是推迟加载,不是提前加载。哪怕你给首屏 <img> 显式加上它,Chrome 仍可能等到滚动前 500px 才发请求——这和预加载目标完全相反。
-
loading="eager"是默认行为,显式写出来也没错,但它只是“不懒”,不等于“提前” -
decoding="async"只影响解码时机(避免主线程卡顿),和下载时机无关 - 用
display: none或负 background-position 隐藏<img>/<div>,只是让元素不可见,下载仍走正常时机(即 HTML parser 解析到该标签时才发起),无法提升优先级 - 这些方法无法监听加载结果,也无法控制并发或失败重试
验证 preload 是否生效的关键点
打开 Chrome DevTools → Network → 勾选 Priority 列。真正生效的 preload 图片,Priority 应显示为 Highest 或 High;若仍是 Medium 或 Low,大概率是以下之一:
-
as属性写错或缺失 -
<link>放在了<body>里,或<title>之后 - 路径大小写、斜杠、查询参数与后续真实
<img src>不一致,导致二次请求 - 误将
fetchpriority="high"加在已用preload的图上——preload 本身已锁定 Highest,再加无意义
最常被忽略的是:首屏图必须同时设 loading="eager",否则 lazy 启发式策略可能提前发一次 Medium 请求,等真正渲染时又补一次,白跑两趟。



















