最规范的首屏关键图预加载方式是<link rel="preload" as="image">,必须置于<head>内且路径静态、绝对,配合fetchpriority="high"及WebP降级方案。

用 <link rel="preload"> 声明首屏关键图最规范
这是目前唯一被 HTML 标准明确支持、语义清晰、且浏览器原生优化的预加载方式。它不渲染、不占布局,只把资源提前放进缓存,Lighthouse 也能正确识别为“关键请求提前触发”。
必须写在 <head> 里,越靠前越好;as="image" 缺一不可,否则浏览器可能当成普通 fetch 处理,丢失图片特有的缓存策略和解码提示;fetchpriority="high" 在 Chrome 101+ 有效,能抬高优先级,旧版忽略无副作用。
-
href必须是静态路径,不能含模板变量(如"./assets/${name}.webp"),否则会字面量请求导致 404 - 相对路径要基于 HTML 文档位置解析:如果 HTML 在
/user/profile.html,就别写href="./images/hero.jpg",应改用href="/images/hero.jpg" - 不要和
loading="lazy"同时用——逻辑矛盾,浏览器行为不可控
new Image() 适合运行时按需预加载
当你需要根据用户行为(比如 hover)、设备特性(如 window.devicePixelRatio)或模块状态决定是否加载某张图时,new Image() 是唯一可控手段。但它不是“写完就完”,顺序和容错必须手动处理。
- 必须先绑定
onload和onerror,再赋值src,否则缓存命中时事件同步触发,回调直接被跳过 - 赋值后立刻检查
img.complete:为true说明已缓存,需立即执行成功逻辑 - 避免在循环中密集
new Image(),浏览器并发请求数通常限 6~8 个,建议用 Promise 队列控制(例如每次最多 4 个) - 别用
new Image().src = url这种简写——失败无法捕获,也无法 await
别把 loading="lazy" 或 display: none 当预加载用
这是高频误用。设置 loading="lazy" 是推迟加载,不是提前加载;哪怕你给首屏 <img> 显式加上它,Chrome 仍可能等到滚动前 500px 才发请求。而 display: none 或负 margin 占位只是让图片“不显示”,但请求时机完全没变,既不提前也不可控。
立即学习“前端免费学习笔记(深入)”;
-
loading="eager"是默认行为,显式写出来只是“取消懒加载”,不等于“预加载” -
decoding="async"只影响解码是否阻塞主线程,和下载时机无关 - CSS background + 负坐标那种“伪预加载”已过时:它随页面一起加载,挤占首屏带宽,且无法感知加载结果
WebP 预加载必须配 fallback,且注意资源优先级
如果你用 <link rel="preload"> 加载 WebP,但用户浏览器不支持(比如老 Safari),又没提供 JPEG fallback,那这张图就彻底白忙一场。而且预加载不是越多越好——浏览器对 preload 请求有优先级调度,乱加一堆非关键图反而会拖慢真正要紧的资源。
- WebP 的
<link>必须搭配<picture>或 JS 特性检测做降级,不能孤零零一个href="/a.webp" - 只对确定会在首屏或交互初期使用的图做 preload,比如 banner、logo、登录页主视觉;轮播图第二帧、详情页缩略图这类,更适合用
new Image()按需触发 - 同一张图不要既
preload又在<img src>里重复声明——浏览器可能发起两次请求(尤其当src是动态生成时)
href 路径解析上下文和 as="image" 的强制要求——这两点出错,<link rel="preload"> 就退化成无效标签,连 Network 面板都看不出异样。



















