图片预加载必须用<link rel="preload" as="image">或new Image(),loading="eager"和decoding="async"均不实现提前下载;前者仅确保立即加载,后者只优化解码时机。

图片预加载不是靠 <img> 标签自动发生的,loading="eager" 或 decoding="async" 都不等于预加载;真要提前进缓存,必须用 <link rel="preload"> 声明,或用 JavaScript 主动创建 Image 对象发起请求。
用 <link rel="preload"> 预加载首屏关键图
这是最轻量、浏览器支持最好、Lighthouse 能识别的预加载方式,适用于 banner、logo、登录页主视觉这类用户一打开就看到的图。
-
as="image"必须写,否则浏览器当普通资源 fetch,不走图片缓存和解码逻辑 - 必须放在
<head>里,越早越好;不能动态插入,也不能在 JS 里拼接href -
fetchpriority="high"在 Chrome 101+ 有效,能抬高请求优先级,旧版忽略无副作用 - 路径必须是静态绝对路径(如
/assets/hero.webp),相对路径易因 HTML 位置错位导致 404 - 它只下载进缓存,不渲染、不占布局,也不触发
onload—— 所以适合“确保资源就位”,但不管“何时展示”
用 new Image() 实现运行时条件预加载
当你需要根据用户行为(比如 hover)、设备像素比(window.devicePixelRatio)或模块加载状态来决定是否预加载时,Image 对象是唯一可控的选择。
- 顺序不能错:先绑定
onload/onerror,再赋值src,否则缓存命中时事件会直接同步触发而跳过回调 - 必须检查
img.complete:为true说明已缓存,需立刻执行成功逻辑 - 避免密集循环
new Image():浏览器并发请求数通常只有 6~8 个,建议加队列控制(例如每次最多 4 个) - 别用
new Image().src = url简写 —— 没法监听失败,也没法await
别把 loading="lazy" 当预加载用
这是最常踩的坑:loading="lazy" 是推迟加载,不是提前加载。哪怕你给首屏 <img> 加了它,Chrome 仍可能等到滚动前 500px 才发请求。
立即学习“前端免费学习笔记(深入)”;
-
loading="eager"只是“不懒”,不等于“提前”;它只是恢复默认行为 -
decoding="async"只影响解码时机(防主线程卡顿),和下载时机完全无关 - 用
display: none或 CSS background 伪隐藏来“骗”浏览器加载,无法监听状态,且可能干扰渲染流程 - 首屏关键图误加
loading="lazy",可能导致白屏或严重延迟 —— 必须删掉,并配<link rel="preload">
真正难的是权衡:预加载太多会挤占带宽,影响首屏其他资源;预加载太少又达不到“秒出”效果。关键不在技术能不能做,而在你是否清楚哪几张图真的“非快不可”——这得结合 Lighthouse 报告、真实用户监控和业务场景一起判断。



















