是最可靠、最易落地的图片预加载方式,适用于明确首屏或交互初期必用的关键图(如banner、logo),需在中静态声明、as="image"必填、路径准确且不可含变量;new Image()适用于运行时条件预加载,须先绑定事件再赋值src并检查img.complete。

直接用 <link rel="preload"> 声明关键图,是最可靠、最易落地的预加载方式;其他手段要么控制力弱(如 CSS 伪预加载),要么容易出错(如 new Image() 顺序写反就丢事件)。
什么时候该用 <link rel="preload">
适用于你**明确知道某张图会在首屏或交互初期立刻用到**的场景,比如 banner、logo、登录页主视觉。它不渲染、不占布局,只把图片下进缓存,且 Lighthouse 能识别。
-
as="image"必须写,缺了浏览器可能当普通 fetch 处理,失去图片专用缓存和解码策略 - 路径必须静态准确:相对路径基于 HTML 文件位置解析,比如 HTML 在
/user/profile.html,就别写./images/hero.jpg,改用/images/hero.jpg或绝对 URL -
fetchpriority="high"在 Chrome 109+ 有效,能抬高请求优先级;旧版忽略无副作用 - 不能在
href里写模板变量,比如"/assets/${name}.webp"会按字面量请求,大概率 404
什么时候该用 new Image()
适用于需要运行时判断是否加载的场景,比如用户 hover 后预载下一页缩略图、根据 window.devicePixelRatio 加载 @2x 版本、或按模块懒加载对应资源。
- 顺序不能错:先绑定
onload/onerror,再赋值src;否则缓存命中时事件同步触发,回调会被跳过 - 检查
img.complete:如果为true,说明已缓存,需立刻执行成功逻辑 - 避免密集
new Image():浏览器并发请求数通常只有 6~8 个,建议加队列或 Promise 控制(例如每次最多 4 个) - 基础封装示例:
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" 是推迟加载,不是提前加载。哪怕你给首屏 <img> 显式加上它,Chrome 仍可能等到滚动前 500px 才发请求——这和预加载目标完全相反。
-
loading="eager"只是“不懒”,不等于“提前” -
decoding="async"只影响解码时机,和下载无关 - CSS
background伪预加载不可靠:它依赖样式计算和渲染时机,Lighthouse 不识别,且可能拖慢首屏渲染 -
display: none的<img>仍是懒加载行为,不是预加载
最容易被忽略的是路径解析规则和事件绑定顺序——<link rel="preload"> 的 href 错一层目录就 404,new Image() 的 onload 写在 src 后面就永远收不到回调。这两个点卡住的人最多。











