Early Hints实战指南:加速网页加载的前沿技术解析
Early Hints 到底值不值得上?我折腾完的真实体验
去年我在一个首屏加载慢得离谱的项目里试了 Early Hints,本来以为就是加个 header 的事,结果折腾了三天才跑通。今天这篇就聊聊我踩过的坑、验证过的写法,还有那些“看起来很美但实际会翻车”的错误用法。
我的写法,亲测靠谱
Early Hints 的核心是让服务器在 HTML 正文还没生成完之前,提前告诉浏览器“待会儿要用到这些资源,你先去预加载”。关键点在于:**只推关键资源,别贪多**。
我一般只推两类东西:首屏 CSS 和关键 JS(比如 React 的主 bundle)。字体、图片、非首屏 JS 一律不推——推多了反而拖慢 TCP 连接,得不偿失。
后端用 Node.js + Express 的话,可以这样写:
app.get('/page', (req, res) => {
// 先发 Early Hints
res.writeEarlyHints({
link: [
'</static/main.css>; rel=preload; as=style',
'</static/app.js>; rel=preload; as=script'
]
});
// 模拟耗时操作(比如查数据库)
setTimeout(() => {
res.send(
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/static/main.css">
</head>
<body>
<script src="/static/app.js"></script>
</body>
</html>
);
}, 300);
});
这里注意:writeEarlyHints 是 Node 18.11+ 才支持的 API,旧版本得用 res.write('HTTP/1.1 103 Early Hintsrn...') 手拼,容易出错。建议直接升级 Node,省事。
如果你用的是 Nginx,也可以在配置里加:
location /page {
add_header Link '</static/main.css>; rel=preload; as=style' early_hints;
add_header Link '</static/app.js>; rel=preload; as=script' early_hints;
proxy_pass http://backend;
}
但注意:Nginx 的 early_hints 指令要求 upstream 也支持 Early Hints,否则可能被忽略。我一开始没开 proxy_http_version 1.1;,结果 hints 根本没发出去,白忙活半天。
这几种错误写法,别再踩坑了
我见过太多人把 Early Hints 当成“万能加速器”,结果适得其反。下面这些写法,我都试过,全翻车了:
- 推一堆非关键资源:比如把所有图片、第三方统计脚本、甚至 Google Fonts 都塞进 Early Hints。浏览器会优先处理这些预加载,反而阻塞了真正关键的 CSS/JS。实测 LCP 反而慢了 200ms。
- 路径写错或重复:比如 Early Hints 里写了
</static/main.css>,但 HTML 里写的是<link href="/static/css/main.css">。路径不一致,浏览器会当成两个资源,白白浪费带宽。更糟的是,如果 HTML 里又重复写了 preload,会导致资源被加载两次(虽然有缓存,但请求还是发了)。 - 在 HTTP/1.1 下硬上:Early Hints 依赖 HTTP/2 或 HTTP/3 的多路复用。如果用户还在用 HTTP/1.1(比如某些企业内网),发 103 响应会被当成非法响应,直接断连。我第一次上线时没做 UA 检测,导致部分老设备打不开页面。后来加了判断:
if (req.httpVersionMajor >= 2) { // 才发 Early Hints }
还有一个隐蔽坑:**CDN 不支持**。我用的某云 CDN 默认会吞掉 1xx 响应,导致 Early Hints 根本到不了客户端。折腾一周才发现要手动开启“透传 1xx 响应”开关(而且要额外付费)。上线前务必在真实 CDN 环境测试!
实际项目中的坑
除了技术细节,还有一些工程上的麻烦事:
首先,**本地开发环境很难模拟**。Chrome DevTools 的 Network 面板能看到 Early Hints,但本地起的 Express 服务默认是 HTTP/1.1,根本触发不了。我后来用 nghttp2 工具手动发请求才验证成功,过程很痛苦。建议直接上测试环境跑,别在本地死磕。
其次,**缓存策略要小心**。Early Hints 里的资源如果设置了强缓存(比如 Cache-Control: max-age=31536000),那没问题。但如果缓存时间短,或者没设缓存,浏览器每次都会重新请求,反而增加负担。我们有个项目因为 CSS 文件名没加 hash,导致 Early Hints 推的永远是旧版本,用户看到样式错乱。后来强制所有静态资源走 content-hash,才解决。
最后,**别指望它能解决所有性能问题**。Early Hints 只能优化“资源发现”的延迟,如果你的 TTFB(Time To First Byte)本身就很慢(比如后端逻辑复杂),那提升有限。我测过一个 TTFB 800ms 的页面,上了 Early Hints 后 LCP 只快了 100ms 左右。先把后端优化做好,再考虑这个。
要不要上?我的建议
如果你的项目满足以下条件,可以试试 Early Hints:
- 首屏依赖的关键资源明确(比如 1~2 个 CSS + 1 个 JS)
- 后端 TTFB > 300ms(否则收益太小)
- CDN 和服务器都支持 HTTP/2+ 且能透传 103 响应
否则,老老实实用 <link rel="preload"> 在 HTML 头部声明,效果差不多,还省心。Early Hints 更像是“锦上添花”,不是“雪中送炭”。
对了,记得用 WebPageTest 或 Lighthouse 测真实效果。别光看 DevTools 的 Timing 图,那个不准。我有一次看到 Timing 里 preloaded 资源变绿了,以为成功了,结果 Lighthouse 报告显示 LCP 没变化——因为资源虽然提前加载了,但渲染还是被 JS 阻塞了。
以上是我折腾 Early Hints 后的血泪总结。这东西水挺深,文档又少,很多细节得靠自己试。如果你有更好的方案,或者踩过我没提到的坑,欢迎评论区交流!
