彻底搞懂 <link rel="preload">:为什么必须声明 as,以及它与 Prefetch 的本质区别
在性能优化中,很多开发者知道 preload 可以“加速加载” ,却不知道 as 是 preload 生效的关键。甚至大量线上项目因为 缺失 as 或 as 错配 反而出现 重复请求、带宽浪费、字体无法复用、首屏变慢 等“性能倒退”。
本文从实际工程角度,系统讲清 preload 的核心机制、浏览器如何利用 as、常见错误、最佳实践,并补充最容易混淆的 preload vs prefetch 区别,确保你能将它真正用在生产环境中。
一、核心结论(直击本质)
<link rel="preload"> 不是“提前执行”,而是告诉浏览器:
这个资源非常关键,请尽快下载,但先不要执行/应用。
而要让浏览器在后续真正使用该资源时 复用这次下载的结果,就必须告诉浏览器:
- 这是 脚本 / 样式 / 字体 / 图片 / fetch 请求 / worker
- 它的用途是什么
- 应用什么优先级、安全策略、缓存分区
这一切都由 as 属性决定。
如果 as 错配或缺失,浏览器就会认为这是一个 无类型资源,导致:
- 下载优先级下降
- 与实际用途的缓存不匹配 → 二次请求
- 字体因缺少 CORS 规则而失效
- 模块脚本无法复用,甚至拒绝执行
换句话说:
preload 是否真正提升性能,90% 取决于 as 是否正确。
二、浏览器为什么需要 as(底层机制讲透)
浏览器会根据 as 做四件关键事情:
1. 决定请求优先级(Loading Priority)
不同资源优先级不同:
| 资源类型 | 优先级 |
|---|---|
| 样式 | 最高(影响渲染) |
| 脚本 | 高 |
| 字体 | 中 |
| 图片 | 低 |
| prefetch | 最低 |
没有 as → 浏览器无法判定 → 预加载意义大幅下降。
2. 应用正确的安全策略 / CORS
例如:
- 字体 preload:必须携带
crossorigin - 脚本 preload:需要按照脚本安全规则处理
- fetch preload:会按 CORS 规则处理请求
无 as → 无法匹配正确策略 → 完成下载却无法复用。
3. 放入正确的缓存“分区”(Fetch group)
浏览器会根据 request destination 存放缓存:
as="script"→ 归类为脚本缓存as="style"→ 样式缓存as="font"→ 字体缓存as="image"→ 图像缓存
如果 preload 没有 as:
→ 浏览器会将其归类为“无类型”缓存
→ 后续真正加载时 不匹配,无法复用
→ 重新请求一次
这是许多站点出现 重复请求 的根本原因。
4. MIME 预期与错误保护
预加载字体时浏览器预期 font/woff2
预加载脚本预期 application/javascript
类型不符 → 直接拒绝复用。
三、一些简单的示例
预加载脚本(最常见)
<link rel="preload" href="/static/app.js" as="script">
<script src="/static/app.js" defer></script>
预加载样式
<link rel="preload" href="/css/main.css" as="style">
<link rel="stylesheet" href="/css/main.css">
预加载字体(必须加 crossorigin)
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
预加载关键图片(谨慎使用)
<link rel="preload" href="/img/hero.jpg" as="image">
四、错误使用 as 会带来哪些严重后果?
下载优先级错误
CSS 或 JS 被当成“低优先级资源”,导致首屏变慢。
缓存不匹配导致重复请求
Network 面板常见的“双份下载”就是这里来的。
字体下载了但无法被 CSS 使用
最典型表现:FOIT / FOUT 现象加重。
带宽浪费、关键资源受阻
尤其是 HTTP/1 上,会严重阻塞关键请求。
五、Preload 与 Prefetch 的区别(最容易混淆,但非常重要)
一图看懂 Preload vs Prefetch
| 项目 | Preload | Prefetch |
|---|---|---|
| 用途 | 加速“当前页面”关键资源 | 提前下载“未来页面”可能需要的资源 |
| 优先级 | 高(参与渲染关键路径) | 极低(不会影响当前页面) |
| 是否立即使用 | 是,将在当前渲染中用到 | 否,未来可能用到 |
| 资源缓存是否可复用 | 高概率复用 | 通常复用 |
| 会不会阻塞当前页面 | 可能 | 绝不会 |
| 示例资源 | CSS、JS、字体、首屏图像 | 下一跳页面的脚本、图片、JSON |
直观理解
- Preload = 当前页面的加速器
- Prefetch = 下一个页面的缓存预热器
如果你把本该 prefetch 的资源 preload → 影响当前首屏
如果把本该 preload 的资源 prefetch → 毫无效果
这是工程上最常见的误区之一。
六、工程化最佳实践
- 只 preload 关键渲染资源(CSS、关键 JS、字体)
- preload font 时 必须加 crossorigin + type
- preload CSS 后仍需
<link rel="stylesheet"> - 模块脚本优先使用
rel="modulepreload" - 不要滥用 preload(会拖慢首屏)
- preload 必须放在
<head>顶部 - 经常检查浏览器是否出现“二次请求”
八、总结
Preload 让浏览器“提前知道”关键资源是什么;
as 让浏览器“提前知道”它要如何被正确处理。
二者缺一不可,否则 preload 不但无效,还可能拖慢性能。