<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 指令校验。
配套属性还有三个该用上:
type:声明 MIME 类型,浏览器不支持时直接跳过。用 AVIF、WOFF2 时应写上,避免不支持该格式的浏览器白白下载。
media:只在匹配媒体查询时才 preload,例如 <link rel="preload" href="/css/print.css" as="style" media="print">。
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 验证收益——没有收益就删掉,别留。