preload用于当前页面“马上就要用”的关键资源,如首屏图片、核心CSS、字体;prefetch用于未来页面可能用到的资源,需等onload后低优先级加载,且依赖缓存与同源等条件。

preload 和 prefetch 不是可互换的性能“开关”,用错一个,首屏就可能变卡、带宽就被白占。
什么时候该用 preload 而不是 prefetch
判断依据就一条:这个资源在当前页面导航中是否「马上就要用」?是 → 用 preload;否 → 用 prefetch 或不用。
-
preload在 HTML 解析早期就触发,浏览器以 High/Highest 优先级抢带宽,适合首屏 Hero 图、关键字体、核心 CSS、即将由import()动态加载的 JS chunk -
prefetch要等window.onload后才发起,且优先级为 Low,常被浏览器跳过(比如用户开了省流模式、内存紧张、网络是 2G) - 把本该
prefetch的商品详情页 JS 写成preload,会导致首页加载时多发一个高优请求,挤占 CSS/JS 解析带宽,LCP 反而延迟 - Chrome DevTools Network 面板里看
Priority列:若写的是preload却显示low,大概率是漏了as属性或路径非法
preload 必须写的三个东西:as、crossorigin、font-display
只写 <link rel="preload" href="font.woff2"> 是无效的——浏览器既不知道怎么设请求头,也无法复用到后续 @font-face 加载中。
-
as="font"是硬性要求,不写会被降级为普通fetch,字体无法复用;as="script"和as="style"同理 - WOFF2 字体必须加
crossorigin,哪怕同源。Chrome 会直接拒绝应用,且控制台不报错,只静默失败 - 仅
preload不够,CSS 中@font-face还得配font-display: swap,否则文本仍会 FOIT(空白等待),预加载白做 - 避免重复请求:不要在
@font-face的src里写同一地址,又在外面preload一次,Safari 尤其容易双下
prefetch 生效的硬性条件和常见失效原因
prefetch 看似简单,但实际生效依赖一连串脆弱条件,写对了也不代表浏览器真会去下。
立即学习“前端免费学习笔记(深入)”;
- href 必须是合法 URL:不能是协议相对路径(如
//cdn.example.com/a.js),会被当成非法协议直接忽略 - 必须同源:跨域
prefetch在 Chrome/Firefox 中基本被无视,即使服务端开了 CORS 也没用 - 强缓存是前提:响应头必须含
Cache-Control: public, max-age=31536000,否则下次跳转时缓存失效,prefetch白做 - 不能和
preload混用同一资源:比如同时preload和prefetchmain.js,Chrome 会发两次请求 - DevTools 里找不到请求?先确认页面是否已触发
onload;再检查 Filter 是否选了All(不是仅 XHR);最后看状态是不是cancelled(用户快速跳走)或net::ERR_FAILED(404 或 CORS)
动态场景下如何安全插入 preload
服务端无法预知的运行时资源(比如根据 UA 加载不同分辨率图片、AB 实验脚本),得靠 JS 主动插入 preload link。
- 必须用
document.createElement('link')创建,rel="preload"、as、href全部设全,然后document.head.appendChild(link) - 越早执行越好,理想位置是
DOMContentLoaded前;晚于这个时机,可能赶不上关键渲染节点 - 插入后浏览器立即发起请求,但不会自动执行(script 不运行,font 不生效),后续需手动处理(比如
link.onload后插入script标签) - 注意查重:插入前先遍历
document.head.querySelectorAll('link[rel="preload"][href]'),避免反复创建相同href的 link 导致内存泄漏
真正难的不是写对标签,而是判断「这个资源到底算不算当前页马上要用」——它需要结合真实用户路径、资源加载时机、构建产物结构来逐个验证,而不是套模板。



















