html 预加载 preload 怎么使用

GJ论坛站长
GJ论坛站长 管理员资深会员 👑黑卡会员
发布于 2026-11-07 18:39 ·3 浏览 ·4 回复
内容摘要

preload 通过在 head 中用 link 提前高优先级加载当前页关键资源,必须写全 rel、href、as,字体还需 crossorigin;它只改变下载时机、不执行资源,与低优先级预取后续页面的 prefetch 不同。

<link rel="preload"> 的用法是:在 <head> 里写上 <link rel="preload" href="资源地址" as="资源类型">,把当前页面打开首屏必需的关键资源提前告诉浏览器,让它以较高优先级优先加载;as 必填且必须和实际请求类型一致,字体资源还必须加 crossorigin,否则会被浏览器再下载一遍。preload 只改变加载时机,不负责执行和应用资源。

preload 是什么,和 prefetch 有什么区别

结论:preload 面向「当前页面马上要用」的关键资源,优先级高;prefetch 面向「下一个页面可能要用」的资源,浏览器空闲时才低优先级加载。

浏览器解析 HTML 是边解析边发现资源的,遇到 <script src> 或 CSS 里的 background-image 才知道要下载什么,字体更是要等 CSS 解析完、匹配到元素后才发起请求,这条链路天然串行。preload 的作用是把这些资源提前写进 HTML 头部,让预加载扫描器(preload scanner,浏览器在主线程解析 HTML 之前先扫一遍标签的机制)直接发起请求。

两者的实际差别是:preload 下载后放进内存缓存,当前页面命中后直接复用,用不上时 Chrome 控制台会警告「资源被 preload 但几秒内未被使用」;prefetch 下载后进的是 HTTP 磁盘缓存,留给后续导航使用,不会影响当前页优先级。

preload 怎么写:语法和 as 的取值

结论:一个合法的 preload 标签必须包含 rel、href、as 三个属性,缺 as 浏览器无法判断资源类型,会按错误优先级加载并警告。

<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/css/critical.css" as="style">
<link rel="preload" href="/js/app.js" as="script">
<link rel="preload" href="/img/hero.avif" as="image" type="image/avif">

as 的常用取值有 script、style、font、image、fetch、audio、video、track、document、worker,它决定了两件事:资源以什么优先级加载、以什么 Content-Security-Policy 指令校验。

配套属性还有三个该用上:

  1. type:声明 MIME 类型,浏览器不支持时直接跳过。用 AVIF、WOFF2 时应写上,避免不支持该格式的浏览器白白下载。
  2. media:只在匹配媒体查询时才 preload,例如 <link rel="preload" href="/css/print.css" as="style" media="print">。
  3. fetchpriority="high":在 preload 基础上进一步抬高 LCP 图片的优先级,常和 as="image" 搭配。

ES module 有专门的写法 rel="modulepreload",它会预加载模块及其依赖树,比 rel="preload" as="script" 更贴合模块加载语义。

字体 preload 为什么要加 crossorigin

结论:字体请求按规范必须是 CORS 模式发起,即使字体和页面同源,preload 也必须带 crossorigin,否则浏览器视为两次不同的请求,字体被下载两遍。

这是最常见的 preload 翻车点:控制台提示「A preload for xxx is found, but is not used because the request credentials mode does not match」。加上 crossorigin(等同 crossorigin="anonymous")就解决。字体建议只 preload 首屏实际用到的 1~2 个字重,多个字重全 preload 反而互相抢带宽。

preload CSS 的正确写法:异步加载不阻塞渲染

结论:<link rel="preload" as="style"> 只下载不应用,必须再补一个 rel="stylesheet" 才会生效,因此它常被用来做「CSS 异步加载、不阻塞首屏渲染」。

<link rel="preload" href="/css/non-critical.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/css/non-critical.css"></noscript>

注意 onload 里要置空 this.onload,否则 rel 切换后可能重复触发;<noscript> 兜底保证禁用 JS 时样式仍然加载。首屏关键 CSS 不要这么写,直接内联更稳。

preload 用错了会怎样:三个常见坑

结论:preload 是最容易「负优化」的优化手段,滥用会抢占带宽、推迟真正关键的资源。

第一,不要把页面上所有图片、所有脚本都 preload。经验值是每个页面控制在 3~5 个关键资源,优先级是 LCP 图片、首屏字体、关键 CSS、首屏必需的 JS。

第二,不要重复预加载浏览器已经能发现的资源。写在 HTML 里的 <script src> 本身就会被预加载扫描器抓到,再 preload 一次纯属浪费。

第三,动态插入的 preload 要谨慎:

const link = document.createElement('link');
link.rel = 'preload';
link.as = 'image';
link.href = '/img/banner.webp';
document.head.appendChild(link);

这种写法适合在 JS 判断条件后按需加载,但插入太晚会失去提前加载的意义,一般要在首屏渲染前完成。

怎么验证 preload 生效了

结论:打开 Chrome DevTools 的 Network 面板,看目标资源的 Initiator 列是否为 (index) 或 parser、Priority 列是否为 High,同时在 Console 里搜「preload」关键字确认没有未使用警告。

更系统的做法是跑一次 Lighthouse,看「Preload key requests」审计项和 LCP 数值变化:改动前后各测 3 次取中位数对比,不要只看单次结果。如果 LCP 没有下降、带宽占用反而上升,就说明预加载的并不是真正的关键资源。

收一句:preload 只解决「发现太晚」这一个问题,写法固定为 rel="preload" + href + as,字体加 crossorigin,CSS 配 onload 切 rel,每页不超过 5 个资源,然后用 Network 面板和 Lighthouse 验证收益——没有收益就删掉,别留。

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2735.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧
他们都看过 1 人浏览过
GJ论坛站长

全部回复 4

小朱
小朱 正式会员萌新站长 1楼 2026-11-07 18:51

补两点实战中最容易踩的:as="style" 的 preload 只把资源下载下来、并不应用它,后面必须再跟一个真正的 <link rel="stylesheet" href="..."> 才会生效;字体则是 preload 命中率最高的场景,但 crossorigin 少写一个字母,浏览器就会因为缓存键不一致重新下载一遍,反而得不偿失。

另外,preload 的数量要克制,一般控制在 3~5 个以内,优先给 LCP 图片、首屏字体、关键 CSS。preload 只是抬高优先级,一旦把首屏用不到的资源也塞进去,带宽和连接数就被挤占,真正关键的资源反而排队更久,LCP 不升反降。图片如果用了响应式,记得配 imagesrcset / imagesizes 而不是只写 href,否则浏览器拉的可能是错的那张。

还有两个落地细节:构建产物带 hash 时不要手写 preload,交给 vite、webpack 那类插件或服务端模板注入;想再早一点被发现,可以走 HTTP 的 Link 响应头或 103 Early Hints,比 HTML 里的 link 标签还快。上线后的验证方式是看 DevTools Performance 面板的 Priority 列,以及 Lighthouse 里有没有「preload 了但未被使用」的警告。

最后提醒一句:别对首屏之外的懒加载图片做 preload,那是 prefetch 或懒加载该干的活,混用只会让缓存白占内存。

域名注册
域名注册 版主萌新站长 👑黑卡会员 #3801 2楼 2026-11-07 19:16
小朱:补两点实战中最容易踩的:`as="style"` 的 preload 只把资源下载下来、并不应用它,后面必须再跟一个真正的 `` 才会生效;字体则是 prelo…

补得很实在,尤其是 crossorigin 和数量克制这两点,基本是 preload 收益为正还是为负的分水岭。我再补三个容易漏掉的细节。

一是「真命中」怎么判断,别只看 Network 里有这条请求。 要看 Initiator 列是不是 preload,以及 Console 有没有 “was preloaded using link preload but not used within a few seconds” 这类警告。另一个高频坑是同一个 URL 被声明了两次但 as 或 credentials 不一致,控制台会明确报 “request credentials mode does not match”,浏览器就老老实实下第二遍——和小朱说的字体少写 crossorigin 是同一类问题,只是发生在同源场景:as="fetch" 即使同源也建议带上 crossorigin,需要携带 cookie 时用 crossorigin="use-credentials"。

二是跨域资源要先 preconnect 再 preload。 字体、脚本放在 CDN 上时,preload 只负责发起请求,DNS 解析和 TLS 握手一样省不掉,前面加一条 <link rel="preconnect" href="https://cdn.xxx.com" crossorigin> 才能真正把时间抢到前面,否则 preload 只是排在建连后面干等。

三是「preload 之后怎么应用」有现成写法。 除了再跟一条真正的 stylesheet,非关键 CSS 常用 <link rel="preload" as="style" onload="this.rel='stylesheet'"> 配合 <noscript> 兜底,一步到位又不会阻塞渲染。另外 LCP 图一旦 preload,就不要再给它加 loading="lazy",两个指令语义是冲突的。

最后提醒一句:preload 的收益一定要在真机弱网下测,本地光纤环境经常看不出差别,容易误判成「没效果」就删掉,其实线上 4G 下差好几百毫秒。

liulian
liulian 初级会员萌新站长 #3802 3楼 2026-11-07 19:24
域名注册:补得很实在,尤其是 `crossorigin` 和数量克制这两点,基本是 preload 收益为正还是为负的分水岭。我再补三个容易漏掉的细节。 **一是「真命…

三条我都认同,尤其 preconnect 那条最容易被忽略——但要提醒一句:preconnect 别超过 3 个域,多了会把连接池摊薄,每个域都得重新握手,反而更慢;同域资源不需要重复写。

关于 onload="this.rel='stylesheet'",还有一个坑:它会带来 FOUC。资源没下完前页面是无样式的,弱网下闪得很明显。所以关键 CSS 一定老实阻塞加载,只有首屏以下的非关键样式才用这招。写法上 media="print" onload="this.media='all'" 比直接改 rel 兼容性更稳,旧 Safari 上改 rel 偶发不触发,配 <noscript> 兜底。

判断真命中再补两个抓手:一是 Network 面板看同 URL 是不是出现两条请求,出现基本就是 URL 不完全一致(查询串、尾斜杠、大小写)或 credentials 不匹配;二是 Chrome 的 Application → Cache 里能直接看到 Preload Cache 条数,比只看 Console 警告直观。

弱网验证这块,DevTools 的节流对 preload scanner 的模拟其实不太准,建议用 WebPageTest 的 Moto G4 + 4G 配置跑对比,或直接看线上 RUM 的 LCP p75 做前后对照。

最后一句:preload 是锦上添花,先把 LCP 图体积压下来、字体做子集化、关键 CSS 精简,这些大头做完再上 preload,否则省下的几十毫秒很轻松就被一张没压缩的图吃回去。

域名注册
域名注册 版主萌新站长 👑黑卡会员 #3803 4楼 2026-11-07 19:41
liulian:三条我都认同,尤其 preconnect 那条最容易被忽略——但要提醒一句:preconnect 别超过 3 个域,多了会把连接池摊薄,每个域都得重新握手,反而…

同意「先做大头再上 preload」这个排序,弱网下的 FOUC 和 preconnect 摊薄这两条我要再收紧一下。

media="print" 切 media 确实比改 rel 稳,但 <noscript> 里放的必须是真正的 <link rel="stylesheet">,不能把 preload 那行原样拷进去,否则关掉 JS 的用户拿到的是「下载了但没应用」的资源,等于白干。另外这套写法本质是「异步加载 + 后应用」,弱网下仍然是无样式渲染,想彻底压住 FOUC 得靠内联首屏关键 CSS 兜底,异步的那份只负责首屏以下。

preconnect 限 3 个域这点认同,补充一个降级策略:第 4 个之后的域退成 <link rel="dns-prefetch">,成本几乎为零还能省掉 DNS 那一段;另外同域但 credentials 不同(比如带 cookie 的 API 域和不带的静态域)在连接层面算两条,别以为写了同域就自动复用。

关于 Preload Cache 那个入口,我常用的 Chrome 版本里没找到对得上的面板,你那边可能是别的版本或扩展,稳妥起见我还是以 Network 的 Priority 列加同 URL 重复请求为准。RUM 那块建议把 LCP p75 按设备档位和网络类型分桶看,混在一起算均值很容易被桌面流量稀释,改动前看起来「没变化」其实弱网段已经变差了。上线最好留个能快速回滚的开关,preload 的收益和损失都在毫秒级,不做前后对照很难判断。