dns-prefetch实战指南:提升网页加载速度的关键技巧
优化前:卡得不行
上周上线一个新页面,测试同事一打开就喊:“这加载也太慢了吧!”我本地跑着还行,但到线上环境,首屏资源依赖了好几个第三方域名——比如 CDN、统计脚本、还有几个跨域的 API 接口。用户点进来,白屏好几秒,然后才慢慢渲染出来。我自己用手机 4G 网络试了下,真的卡得想砸手机。
最离谱的是,明明主资源都缓存了,但每次刷新还是要等 3 秒以上。我一开始以为是接口慢,结果查了网络面板才发现,光 DNS 查询就花了 600ms~1s。好几个外部域名(比如 https://jztheme.com/api/data 这种)都是首次访问,浏览器得先查 DNS,再建 TCP,再 TLS 握手……这一套下来,用户早跑了。
找到瓶颈了!
打开 Chrome DevTools 的 Network 面板,把“Timing”列展开,一眼就看到问题:好几个请求的“Queueing”后面跟着一大段“Stalled”,点进去一看,全是 DNS lookup 耗时。尤其是那些第三方服务,比如字体 CDN、埋点服务、还有我们调用的外部 API,每个都要单独做一次 DNS 解析。
我数了下,页面里至少有 5 个不同的外部域名。在弱网环境下,每个 DNS 查询平均要 300ms,加起来就是 1.5 秒打底。这还没算 TCP 和 TLS 的时间。难怪用户觉得慢。
这时候我想到以前看过的一个冷门但实用的优化手段:dns-prefetch。说白了,就是提前告诉浏览器:“待会儿我要用这个域名,你先去查一下它的 IP,别等要用的时候再查。”
核心代码就这几行
其实用法特别简单,就在 HTML 的 <head> 里加一行 link 标签:
<link rel="dns-prefetch" href="//jztheme.com">
注意,这里用的是双斜杠开头,不写协议(http/https),这样能兼容当前页面的协议,避免混合内容警告。而且,dns-prefetch 只关心域名解析,不涉及后续的连接建立,所以开销极小,几乎不会影响主流程。
我一口气把所有外部域名都加上了:
<head>
<!-- 其他 head 内容 -->
<link rel="dns-prefetch" href="//cdn.example-cdn.com">
<link rel="dns-prefetch" href="//analytics.vendor.com">
<link rel="dns-prefetch" href="//jztheme.com">
<link rel="dns-prefetch" href="//fonts.googleapis.com">
</head>
这里注意我踩过好几次坑:别把内部域名也加进去!比如你自己的 api.yoursite.com,如果和主站同源,DNS 早就解析过了,加了反而浪费。只加那些**跨域且首次访问**的第三方域名。
另外,有些老项目还在用 <meta http-equiv="x-dns-prefetch-control" content="on"> 来开启自动 prefetch,但现代浏览器默认就是开启的,这行基本可以删了。除非你明确禁用了它(比如出于隐私考虑),否则不用管。
踩坑提醒:这三点一定注意
- 别滥用:不是所有外部链接都值得 prefetch。如果某个域名只是 footer 里一个不显眼的友情链接,用户根本不会点,那加了就是白费。只加那些**首屏或关键路径上一定会用到**的域名。
- HTTPS 页面慎用非安全域名:虽然
dns-prefetch本身不传输数据,但如果在 HTTPS 页面里 prefetch 一个 HTTP 域名,某些严格模式的浏览器可能会报 mixed-content 警告。稳妥起见,尽量只 prefetch 你确定会用到的、且支持 HTTPS 的域名。 - 移动端效果更明显:我在桌面端测,优化前后差距不大(因为本地 DNS 缓存快),但在 4G 网络下,prefetch 能省下 800ms 以上的等待时间。所以一定要用真机+弱网环境测试。
优化后:流畅多了
改完之后,我用 WebPageTest 跑了两组对比(模拟 3G 网络,Moto G4 设备):
- 优化前:首屏完全加载时间 4.8s,其中 DNS lookup 总耗时 1.2s
- 优化后:首屏完全加载时间 3.1s,DNS lookup 总耗时 0.3s
整整快了 1.7 秒!而且从用户感知上,白屏时间明显缩短,内容“唰”一下就出来了。虽然最终加载时间还是受接口响应影响,但至少 DNS 这块的卡顿彻底没了。
有意思的是,prefetch 的效果在二次访问时反而不明显——因为 DNS 已经缓存了。但它对**首次访问用户**的提升是巨大的,而这部分用户恰恰是最容易流失的。
性能数据对比
为了更直观,我整理了 Lighthouse 的评分变化(同样是 3G 模拟):
| 指标 | 优化前 | 优化后 |
|---|---|---|
| First Contentful Paint (FCP) | 2.9s | 1.8s |
| Largest Contentful Paint (LCP) | 4.6s | 3.0s |
| Overall Performance Score | 58 | 76 |
虽然 dns-prefetch 不是万能药,但在这个场景下,它几乎是“零成本高回报”的典型。改一行 HTML,就能提升十几分的性能分,何乐不为?
最后说两句
当然,这个方案也不是完美的。比如,如果用户根本没触发某些功能(比如没点开需要加载额外资源的弹窗),那 prefetch 就算白做了。但考虑到 DNS 查询的开销极小,且只在空闲时执行,这点损耗完全可以接受。
另外,如果你的项目已经用了 preconnect,那优先级更高——它会直接建立 TCP/TLS 连接,比单纯的 DNS prefetch 更激进。但 preconnect 有连接数限制(通常 6 个),而且开销大,所以一般只用于最关键的 1~2 个域名。而 dns-prefetch 可以放心多加几个,作为兜底策略。
以上是我踩坑后的总结,希望对你有帮助。如果你有更好的方案,比如结合 resource hints 动态注入,或者用 JS 控制 prefetch 时机,欢迎评论区交流!

暂无评论