跳到主要内容

预加载

面试速答(30 秒版 TL;DR)

  • 预加载(Preload) 是告诉浏览器:这个资源是当前页面很快就要用到的,请尽早拉取。
  • 它解决的是 “浏览器发现得太晚” 的问题,常用于关键字体、LCP 图片、首屏脚本、CSS 背景图。
  • 最常见写法:
<link rel="preload" href="/fonts/app.woff2" as="font" type="font/woff2" crossorigin />
  • 面试里最好顺带区分:
    • preload当前页面 很快要用
    • prefetch未来导航 可能会用

一、心智模型:它为什么有用?

浏览器并不是一开始就知道页面后面会用到哪些资源。

很多资源只有在解析到某个位置时,浏览器才“发现”:

  • CSS 背景图,要等 CSS 下载并解析后才知道
  • 字体文件,要等 CSSOM 建好并命中字形后才知道
  • 动态 import 的模块,要等 JS 运行到对应逻辑才知道

这就会造成一个经典问题:

资源本身很关键,但浏览器发现它的时间太晚。

preload 的作用,就是把“晚发现”改成“早发现”。

先记住这个判断:关键资源如果发现得晚,就要考虑 preload。


二、它最适合解决哪几类场景?

1. 首屏字体

字体常常属于“视觉上很关键,但发现得偏晚”的典型资源。

<link rel="preload" href="/fonts/app.woff2" as="font" type="font/woff2" crossorigin />

2. LCP 图片

比如首页 Hero 图、商品主图、文章头图。如果这张图就是 LCP 元素,提前预加载通常比盲目懒加载更合理。

<link
rel="preload"
as="image"
href="/images/hero.webp"
imagesrcset="/images/hero-800.webp 800w, /images/hero-1600.webp 1600w"
imagesizes="100vw"
/>

3. CSS 背景图

背景图不会像 <img> 一样在 HTML 里直接暴露给浏览器,往往发现得更晚。

<link rel="preload" as="image" href="/images/banner-bg.webp" />

4. 首屏关键脚本

如果某段脚本虽然不是阻塞解析的同步脚本,但很快就要执行,可以考虑预加载后再配合正常的脚本引用。


三、preloadprefetchmodulepreload 怎么区分?

这是这一题最常见的扩展追问。

能力用途资源属于谁时机
preload当前页面马上要用当前导航尽早拉取
prefetch未来可能会用下一次导航或低优先级未来资源浏览器空闲时
modulepreload提前拉取 ES Module 及其依赖当前页面模块图模块执行前

推荐答法:

  • preload 是现在就要。
  • prefetch 是以后可能要。
  • modulepreload 是给 ES Module 体系准备的更专项能力。

如果面试官只问 preload,你把 prefetch 区分出来,答案就会更完整。


四、写 preload 时最容易踩的点

1. as 不能乱写

as 不只是注释,它会影响浏览器如何给资源分类、优先级怎么分配、缓存能不能正确复用。

常见值包括:

  • as="style"
  • as="script"
  • as="font"
  • as="image"
  • as="fetch"

如果 as 写错,可能出现:

  • 资源优先级不符合预期
  • 响应缓存无法按正确类型复用
  • 浏览器把它当成“预加载了但没真正用上”

2. 字体常常要配 crossorigin

字体文件即使同源,也常按跨域取用规则处理。工程里最稳妥的做法通常是把 crossorigin 一并写上。

3. 预加载不等于执行

很多人误以为:

  • preload 了 JS,就等于脚本已经执行
  • preload 了 CSS,就等于样式已经生效

这都不对。

preload 只是 提前下载,真正生效还要靠正常消费它的标签或逻辑。

例如:

<link rel="preload" href="/app.js" as="script" />
<script defer src="/app.js"></script>

上面这个流程是:

  1. 浏览器先把 /app.js 下载好
  2. 真正执行仍然发生在 <script> 阶段

五、为什么说它不是越多越好?

因为 preload 本质是在提高资源优先级。优先级一抬高,就会去抢带宽、抢连接、抢调度机会。

如果你把很多非关键资源也标成 preload,就会出现:

  • 真正关键的资源被带宽竞争拖慢
  • 首页请求瀑布变得更拥挤
  • 浏览器报警“预加载了但很久没用到”

一个很实用的判断标准是:

这个资源如果不提前请求,是否真的会拖慢当前页面关键路径?

如果答案并不明确,就别急着加。


六、一个更完整的页面示例

<head>
<link rel="preload" href="/fonts/app.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/images/hero.webp" as="image" />
<link rel="preload" href="/main.css" as="style" />

<link rel="stylesheet" href="/main.css" />
<script defer src="/app.js"></script>
</head>

这里的意图是:

  • 字体:提前下载,避免首屏文案等待字体
  • Hero 图:尽快到达,争取更好的 LCP
  • 主样式:尽早拿到,缩短渲染等待

但如果这个页面还有一堆评论区脚本、推荐位图片、图表库,它们通常就不该一起 preload


七、典型题与标准答法

Q1:什么是预加载?

答法:

预加载就是通过 rel="preload" 告诉浏览器,某个资源是当前页面很快就要用到的关键资源,请尽早拉取。它主要解决浏览器“发现资源太晚”的问题,常见于字体、LCP 图片、背景图和关键脚本。

Q2:它和 prefetch 有什么区别?

答法:

preload 面向当前页面关键路径,优先级更高;prefetch 面向未来导航,通常在浏览器空闲时做低优先级获取。一个是“现在就要”,一个是“后面可能要”。

Q3:为什么 preload 不能乱加?

答法:

因为它会提高资源调度优先级。如果把一堆非关键资源也标成高优先级,就会造成带宽竞争,反而拖慢真正的关键资源,属于典型的过度优化。

Q4:preload 了 JS,是不是脚本就执行了?

答法:

不是。preload 只负责提前下载,真正执行仍然依赖后续的 <script> 或模块消费流程。


八、常见误区

  • 误区 1:把 preload 当成“无脑加速开关”。 它只适合关键路径资源。

  • 误区 2:as 随便写。 这会影响优先级和缓存复用。

  • 误区 3:首屏外资源也一律 preload 这通常是在制造带宽竞争。

  • 误区 4:把“提前下载”和“立即执行”混为一谈。 预加载不等于执行、不等于渲染完成。


九、速记要点

  • preload = 当前页面马上要用,尽早下载。
  • 核心价值:解决关键资源发现过晚。
  • 典型对象:字体、LCP 图片、背景图、关键脚本。
  • as 要写对,字体常配 crossorigin
  • 不是越多越好,避免关键资源被错误竞争。