preload 和 new image() 是真正能控制加载时机的两种核心手段;前者适用于首屏必用图且需写在 head 中并指定 as="image",后者适用于运行时判断场景且须先绑定事件再赋值 src。

preload 和 new Image() 是真正能控制加载时机的两种核心手段,其他方式(比如 loading="eager"、display: none、CSS background)都不算预加载,只是“不懒”或“伪触发”。
什么时候必须用 <link rel="preload">
只适用于你**100%确定这张图会在首屏渲染完成前就用到**的场景,比如 banner 图、登录框主视觉、关键 icon。它必须写在 里,且越靠前越好。
-
as="image"缺一不可——漏了浏览器当普通 fetch 处理,无法复用图片缓存和解码策略 - 路径必须是绝对路径或根相对路径(如
/assets/hero.webp),不能写./images/hero.jpg,否则 HTML 在子目录下会 404 -
fetchpriority="high"对 Chrome 109+ 有效,能抢在部分 CSS/JS 前发起请求;旧版忽略,无副作用 - 别和
loading="lazy"同时用——逻辑冲突,Chrome 可能直接跳过preload
什么时候必须用 new Image()
适用于需要运行时判断的场景:hover 商品卡片预载详情图、轮播滑到第 3 页时预载第 4/5 页、按 window.devicePixelRatio 加载 @2x 版本。
- 顺序不能错:先绑定
onload/onerror,再赋值src;否则缓存命中时事件同步触发,回调被跳过 - 检查
img.complete === true——为 true 说明已缓存,需立刻走成功逻辑,不能等事件 - 并发要节制:同域默认最多 6–8 个并发连接,批量预加载建议用 Promise 队列,每次最多 4 个
- 别写成一行:
new Image().src = url——失败不可知,也无法 await,必须保留引用并监听事件
哪些“看起来像预加载”的做法其实是错的
这些操作不会提前下载资源,只是制造了“已经加载了”的假象。
-
loading="lazy"是推迟加载,不是预加载;哪怕加在首屏<img>上,Chrome 仍可能等到滚动前 500px 才发请求 -
loading="eager"是默认行为,显式写出只是防继承污染,它不改变发起时机,只表示“不延迟” -
decoding="async"只影响解码是否阻塞主线程,和下载时机完全无关 - CSS
background+-9999px或display: none的<img>:浏览器通常会推迟加载,直到元素进入渲染流或布局计算阶段,无法保证提前
preload,交互后图用 new Image(),确定必显但非首屏的图用 <img loading="eager">,三者混用才叫分阶段预加载。路径写错、事件绑定顺序颠倒、并发失控,这几个点最容易在线上突然暴露。











