首屏加载快的关键是提前建立连接和预加载资源。preconnect需对确定使用的第三方域名使用并加crossorigin属性,不超过6个;preload主Bundle须用as="script"且置于head最前;modulepreload适用于ES模块项目,自动递归预取依赖;CSS和字体preload需正确设置as和crossorigin属性,服务端MIME类型必须匹配,构建产物应固定文件名以确保缓存复用。

首屏加载快不快,关键不是等资源“用到时再拉”,而是让浏览器在解析 HTML 的第一毫秒就知道:哪些域名要连、哪些文件必须立刻下。用对 link 标签的 rel 属性,能抢出 400–800ms,但错一个参数就等于没写。
preconnect 要连谁、怎么连才不白费
跨域资源(比如 CDN、字体服务、API 域名)的 DNS + TCP + TLS 三步握手常耗时 300ms+。preconnect 比 dns-prefetch 更进一步,直接走完这三步。
- 只对确定会用的第三方域名用,例如:
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> - 务必加
crossorigin属性——即使同源,Safari 也会忽略不带它的preconnect - 别超过 6 个,浏览器并发连接数有限,多了会抢主站资源,反而拖慢首屏
- 连完别立刻发请求,建议间隔至少 100ms,确保连接真正就绪
preload 主 Bundle 必须配 as="script",且位置不能错
大型单页应用(SPA)的首屏 JS(如 main.js)如果用 <script src="main.js"></script> 引入,下载会晚 400–800ms;而 <link rel="preload" href="main.js" as="script"> 在 HTML 解析初期就发起请求,和解析并行。
- 必须放在
<head>最前面,紧贴<meta charset>后,<title>前;放后面或动态插入完全无效 -
as="script"是硬门槛,写成as="module"、as="fetch"或留空,Chrome Network 面板里 Priority 就是Low,等于白写 - 路径必须和后续执行时的
src完全一致(含 hash、查询参数),否则重复下载 - 如果用了
integrity,preload和script标签都得带相同值,否则缓存不复用
modulepreload 才是 ES 模块项目的正确选择
如果你的 main.js 是 type="module",且内部有 import { render } from './core.js' 这类静态 import,仅用 preload 不够——它只下 main.js,子模块还得等解析完才逐个请求,多轮 RTT 延迟明显。
立即学习“前端免费学习笔记(深入)”;
- 改用
<link rel="modulepreload" href="main.js">:它会预解析main.js并递归预取所有静态import的依赖(如core.js、router.js) -
modulepreload不允许加as属性,加了会被忽略 -
href必须和type="module"标签里写的路径完全一致(包括大小写、斜杠、查询参数) - 多个
modulepreload标签无顺序依赖,但必须全部出现在首个<script type="module">之前
预加载 CSS 和字体最容易静默失效
preload 对字体和关键 CSS 效果显著,但也最容错——漏一个属性,Network 面板里 Initiator 显示为 (Other)、Priority 是 Low,就是典型失效信号。
- CSS 预加载必须用
as="style"(不是"stylesheet"),并手动切换:<link rel="preload" href="main.css" as="style" onload="this.onload=null;this.rel='stylesheet'"> - 字体必须同时写
as="font"和crossorigin(哪怕同源也必须加),否则加载完也不会应用 - 不要对普通
<img src>加preload,不如直接设fetchpriority="high"更简单有效 - 服务端返回的 MIME 类型必须是
application/javascript(JS)、text/css(CSS)、font/woff2(字体),Safari/Edge 否则可能拒绝执行或渲染
构建产物带 hash(如 app.a1b2c3.js)会让 preload 失效——每次构建路径都变,缓存不复用,甚至指向 404。禁用 hash 或通过构建配置(如 Vite 的 build.rollupOptions.output.entryFileNames)固定首屏 Bundle 名称,才是落地前提。



















