Skip to content

三种资源获取方式的语义和时机差异:prefetch、preload、new Image() ​

一句话区分 ​

  • preload:告诉浏览器"这个资源当前页面很快就要用,尽早加载"
  • prefetch:告诉浏览器"这个资源这页暂时不用,但下一个页面/后续交互可能会用,空闲时再下"
  • new Image().src:不是浏览器资源提示,而是你自己主动发起真实图片请求

一、preload ​

html
<link rel="preload" as="image" href="/banner.jpg">

特点 ​

  1. 高优先级提示

    • 浏览器在解析 HTML 早期就能看到它
    • 会尽早把它加入当前页面的关键资源队列
    • 适合首屏很快会出现的图片
  2. 面向"当前导航"

    • 它服务的是当前页面,而不是下一页
    • 如果这个资源确实马上会被 <img>、CSS 背景图等使用,收益很明显
  3. 有明确语义

    • as="image" 告诉浏览器这是图片资源
    • 浏览器会按资源类型做更合理的调度、缓存和优先级管理
    • 不同 as 值(image、style、script、font 等)的优先级策略也不同

适用场景 ​

  • 首屏 banner 图
  • 首屏背景图
  • LCP(Largest Contentful Paint)图片
  • 路由切进来后马上展示的大图

风险 ​

  • 不要滥用
  • 如果一次 preload 很多张图,会抢占网络连接,反而拖慢真正关键的 JS/CSS/API
  • 如果 preload 的图片最终没被用到,会产生浪费

二、prefetch ​

html
<link rel="prefetch" as="image" href="/next-page-cover.jpg">

特点 ​

  1. 低优先级、空闲时下载

    • 浏览器通常会在当前页面关键资源都差不多完成后,再考虑它
    • 它更像"顺手提前缓存"
  2. 面向"未来可能会用"

    • 更适合下一页、下一步交互、未来可能展示的资源
    • 不是为了当前页面立即渲染
  3. 提示性更强,约束更弱

    • 浏览器可以选择做,也可以因为网络状况、省流模式、优先级策略而弱化甚至忽略
    • 不像 preload 那么"强提醒"

适用场景 ​

  • 首页提前准备详情页首图
  • 列表页提前准备用户最可能点击的下一页图片
  • 多步流程里下一步会出现的插图

风险 ​

  • 它不保证一定马上下载
  • 不适合拿来解决"当前页面马上要显示但来不及加载"的问题
  • 在弱网下,浏览器可能基本不理它

三、new Image().src ​

js
const img = new Image()
img.src = '/cover.jpg'

或者项目里这种写法:

js
const warmupImage = (src) => new Promise((resolve) => {
  const image = new Image()
  const done = () => resolve()
  image.onload = done
  image.onerror = done
  image.src = src
})

特点 ​

  1. 这是实际请求,不是提示

    • 一旦 src 被赋值,浏览器就会真正开始请求图片
    • 不是建议,不是 hint,而是你自己发起加载
    • 从浏览器角度看,这和你写 <img src="/cover.jpg"> 没有本质区别
  2. 由业务代码控制时机

    • 你可以在点击后、页面挂载后、空闲时、滚动到某区域后再加载
    • 也可以自己控制并发、重试、分批预热
    • 项目里的 warmupImagesSimple 就是把这个能力封装成了可配并发数的批量预取
  3. 更灵活,但语义更弱

    • 浏览器不知道它是"当前关键资源"还是"未来可能资源"
    • 只是一个普通图片请求
    • 优先级调度主要取决于浏览器对图片请求的默认策略,以及触发时机

适用场景 ​

  • 你想自己控制图片预热节奏
  • 你要分批加载,避免一次性打满连接
  • 你希望在某个交互后手动缓存下一批图片
  • 图片列表较多,不适合全塞到 <head>

风险 ​

  • 如果你在很早的时机对很多图片都 new Image(),其实和"暴力并发请求"没区别
  • 它不会天然比 preload 更聪明,反而更依赖你的调度策略
  • 写不好容易和页面本身的关键资源争抢带宽

四、核心区别:谁在做调度 ​

preloadprefetchnew Image().src
调度者浏览器浏览器应用代码
触发方式声明式标签声明式标签脚本主动调用
优先级高低普通图片请求
语义当前页关键未来可能用无额外语义
控制力弱(交给浏览器)弱(交给浏览器)强(自己控制)
加载保证浏览器会尽快加载不保证,空闲时才做立即发起实际请求

这就是三者最本质的差异:

  • preload:你给出"当前关键资源"信号,浏览器尽早拉取
  • prefetch:你给出"未来可能用到"信号,浏览器空闲时再处理
  • new Image().src:你不是在"提示"浏览器,而是在"直接下命令发请求"

五、放到这个项目里怎么理解 ​

这个项目实际上在演示三种不同策略:

A. rel="preload" ​

preloadImagesSimple 插件做的事情:把图片列表以 <link rel="preload"> 的形式注入到 HTML 的 <head> 里。

适合:

  • 当前页马上要展示的几张关键图
  • 想在进页面前就把关键图请求发出去

这个项目首页不展示图片,进入 /gallery 时再看图片,正好方便观察:

  • preload 是否在页面真正渲染前就已经开始请求

B. rel="prefetch" ​

preloadImages 插件支持配置 attrs.rel 为 'prefetch',把 <link rel="preload"> 换成 <link rel="prefetch">。

如果改成 prefetch,你会发现:

  • 请求通常更靠后
  • 优先级更低
  • 更像"浏览器有空才帮你存一下"

C. new Image().src ​

warmupImagesSimple 插件的本质:

  • 扫出图片列表
  • 注入一段脚本
  • 用 new Image() 分批请求
  • 每批并发数可控

它更像"程序员自己做资源调度器"。


六、该怎么选 ​

选 preload ​

当资源满足这两个条件:

  1. 当前页面很快就会用到
  2. 它足够关键,值得提前抢占下载时机

比如:首屏主视觉图、LCP 图。

选 prefetch ​

当资源满足这两个条件:

  1. 当前页面不急用
  2. 下一跳或后续交互大概率会用到

比如:列表页跳详情页的头图。

选 new Image().src ​

当你需要:

  1. 手动控制时机
  2. 手动控制并发
  3. 对大量图片做渐进式缓存预热

比如:相册页下一批图片、长列表、游戏素材、故事页分章节图片。

一个最实用的判断标准 ​

你可以直接按这个思路判断:

马上展示 → preload

以后可能展示 → prefetch

我要自己掌控加载节奏 → new Image().src


七、容易混淆的点:preload vs new Image() ​

preload 和 new Image() 都可能"提前把图片下下来",但它们不是一回事:

  • preload 是 HTML 层的资源提示,更早进入浏览器资源调度。浏览器在解析 HTML 的 <head> 阶段就能看到它,此时页面 body 还没开始渲染。
  • new Image() 是 JS 层主动发请求,什么时候发、发多少,由你的代码决定。但它需要等 JS 下载、解析、执行到那行代码时才会触发。

所以:

  • 少量关键图,优先考虑 preload
  • 大量非关键图,通常更适合 new Image() 分批预热
  • 下一页资源,优先考虑 prefetch

八、总结 ​

这个项目最值得讨论的不是"哪个插件更高级",而是这三种策略分别对应三类目标:

策略核心目标类比
preload加速当前页关键图片首屏展示提前把菜端到桌上
prefetch为未来页面或后续交互做低优先级准备先买好菜放冰箱
new Image()用业务代码主动做缓存预热,可控并发自己下厨,按自己的节奏做

在真实项目里,这三种方式经常不是互斥的,而是组合使用:

  • 首屏 1~2 张关键图用 preload
  • 下一页高概率资源用 prefetch
  • 其余批量图片用 new Image() 在空闲时分批预热

本文档与项目三插件对应关系:

  • preload → preloadImagesSimple / preloadImages
  • prefetch → preloadImages(配置 attrs.rel = 'prefetch')
  • new Image().src → warmupImagesSimple