微信客服
Telegram:guangsuan
电话联系:18928809533
发送邮件:[email protected]

为什么使用了CDN反而导致网页LCP分数变差

本文作者:Don jiang

使用 CDN 后 LCP 反而变差,通常是因为资源分发策略或配置问题。很多网站在接入 CDN 后,LCP 元素(如首屏大图、Hero Banner)仍从源站加载,或未开启 HTTP/2、Brotli 压缩和缓存预热,导致请求增加 100–400ms。实际案例中,未优化的 CDN 会让 LCP 从 2.1s 上升到 3.0s 以上。

解决方法包括:将 LCP 图片缓存到 CDN 边缘节点、启用 preload、优化 DNS 解析(减少约 50–120ms),并避免跨区域节点回源,从而稳定把 LCP 控制在 Google 建议的 2.5s 以内。

将 LCP 图片缓存到 CDN 边缘节点

用户打开页面时,LCP 常常就是首屏大图。如果这张图在离用户最近的 CDN 节点里已经有缓存,请求通常只走一段短链路;如果没命中,就要再回源一次,多出 1 次到源站往返和源站处理时间。 对跨洲访问来说,这一步常见就是 200–800 毫秒;在 4G 网络或冷缓存场景里,LCP 很容易从 2.3 秒拖到 3 秒以上。Google 仍以 2.5 秒内 作为良好 LCP 目标。

三类配置问题

把 LCP 图片放到 CDN 域名下,不等于边缘节点一定会留住它。Cloudflare 的默认行为是先参考源站返回的缓存头;没有单独设置 Edge Cache TTL 时,边缘缓存是否保留、保留多久,都受 Cache-ControlExpires 影响。用户看到的结果很简单:同一张首屏图,上午打开是 1 次本地命中,下午再打开可能已经过期,又走了 1 次回源。LCP 的良好线仍是 2.5 秒内,并且看 75% 页面访问 是否达标;多出 300–700 毫秒,页面就可能从 good 掉到 needs improvement。

先看缓存头和边缘 TTL。源站给图片返回 max-age=60,边缘节点大概率只会把它当成 60 秒左右的短缓存对象;首屏图每分钟就可能重新过期一次。用户量不大的页面,首批访问者经常拿到的是冷缓存。活动页、专题页、注册页最常见:发布后前 10–30 分钟 流量不够密,很多 PoP 节点缓存还没热起来,欧洲、北美、澳洲访问者会陆续触发第一次回源。Cloudflare 也提供了单独的 Edge Cache TTL 规则,文档示例里就有把资源放到边缘缓存 1 天 的配置方式;没做这一步,边缘层通常只是“看源站脸色”。

读到这里,问题已经从“有没有 CDN”变成了“CDN 缓了多久、在哪些节点里缓存稳定”。接下来更常见的一层是 URL 自己把缓存打散。业务侧觉得是同一张 Hero 图,CDN 侧看到的却可能是 4 个、8 个、16 个不同对象:

  • /hero.avif?w=1280

  • /hero.avif?w=1280&utm=email

  • /hero.avif?w=1280&ref=ad-7

  • /hero.avif?w=1280&session=8f3...
    只要查询参数进入缓存键,边缘节点就会为每个版本分别建缓存。用户从邮件、广告、自然搜索进入时,虽然看到的是同一块首屏区域,背后却可能对应不同 URL,于是命中率被拆碎。Cloudflare 的缓存配置文档专门提供了自定义 cache key 与 cache rules,就是为了解决这类“资源相同、键不同”的情况。

URL 被拆碎后,用户体感会出现很具体的差异,而且很容易误判成“网络随机慢”。下面三种现象很常见:

  • 同一落地页,桌面端 LCP 2.2 秒,移动广告流量变成 3.0 秒

  • 首页稳定,带 campaign 参数的入口慢 400–900 毫秒

  • A/B 只改文案,图片文件没变,首屏图却再次回源

  • 回访用户快,新访客慢,差距常见在 0.3–1.0 秒
    这些差别不会平均消失,因为 Chrome 统计的是实际访问数据,LCP 阈值又是按 75 分位看;只要一批流量入口持续命中差,报表就会长期偏高。

再往下看,第三类问题不是“缓存不到”,而是“发现太晚”。图片即使已经在边缘节点里,也可能因为浏览器晚发现它而错过首屏时机。Chrome 对 LCP 的拆解里明确包含资源发现和资源加载两个阶段;LCP 图如果写在 CSS background 里,浏览器通常要先拿到 CSS、解析 CSS,才知道还要去请求这张图。若图片是脚本执行后再插入 DOM,请求时点还会继续后移。于是用户看到的是:HTML 已经到了,文本也已经有了,首屏大图还空着 200–600 毫秒

这个阶段最容易出问题的写法,通常集中在下面几类:

  • 首屏图做成 CSS background-image

  • 轮播组件先加载脚本,首图由 JS 再插入

  • 响应式图片存在,但没有预加载首张实际会显示的资源

  • <img> 没有 fetchpriority="high",浏览器按默认优先级处理

  • 同时预加载了 5–10 个资源,结果谁都不够早
    MDN 对 fetchpriority 的说明很明确:它只是一个优先级提示,不保证绝对顺序,但能让浏览器更早处理那张图。web.dev 也提醒过,预加载确实能改善 LCP 图的时机,但预加载过多资源会把优先级效果冲淡。

把这三类问题放在一条链路里看,会更容易理解用户为什么会觉得“已经有 CDN 了,页面还是慢”。一次真实访问常常是这样发生的:浏览器先拿到 HTML,用 100–300 毫秒 解析首屏结构;如果首图藏在 CSS 或 JS 后面,再多等 1 个 CSS 请求或 1 个 JS 初始化;浏览器终于发现图片 URL,但 URL 带了额外参数,没有命中现有边缘缓存;CDN 节点回源,再加上源站处理时间,多花 200–800 毫秒;最后图片开始下载与解码。每一步单独看都不算夸张,叠加后 LCP 很容易多出 0.6–1.4 秒

从实现层面看,正文里可以把排查顺序写得更具体一点,读者一眼就能对照自己站点:

  • 看响应头:首屏图的 Cache-Control60 秒5 分钟 还是 1 天以上

  • 看边缘策略:是否单独给 Hero 图设了 Edge Cache TTL

  • 看缓存键:营销参数、会话参数是否进入 key

  • 看发现时机:首图是 HTML 原生 <img>,还是脚本晚插入

  • 看优先级:是否给首图加了 fetchpriority="high"

  • 看预加载:是否只预加载 1 张首屏会显示的图,而不是一组图库
    这类检查项都能对应到公开文档里的行为说明,不是靠猜。

第一次访问来自 London,首图 URL 带广告参数,边缘 MISS,回源到美国东海岸,LCP 到 3.1 秒;第二次访问来自同一区域,参数被清理,边缘 HIT,浏览器又在 HTML 里立刻发现 <img>,LCP 回到 2.2 秒。两次访问内容几乎一样,差的是缓存头、缓存键和发现时机。

通常看到这种对比,马上就能明白:问题不在“图片格式没换成 WebP/AVIF”,而在请求路径上多走了多少步。Google 的公开建议仍然是把 LCP 压到 2.5 秒内,而 web.dev 也提到,40% 的站点还达不到推荐阈值,LCP 本身就是最难处理的一项 CWV。

继续往前收一层,文件尺寸也会让前三类问题更明显。即便图片已经命中边缘缓存,若首屏实际下发是 700 KB1.2 MB,在移动网络里下载和解码仍会拖长 LCP;但同样大小的文件,在 HIT 和 MISS 场景里给用户的差异更大,因为 MISS 先多出一段回源,再开始传输文件。web.dev 的响应式图片文档反复强调,浏览器应拿到“适合当前设备”的图,而不是始终给桌面大图;不然在 390 px 宽的手机上下载 1600 px 图片,本身就在增加首屏负担。

把内容写到这里,已经能看到一条完整逻辑:边缘 TTL 过短,节点留不住图;URL 太碎,同一张图被分散成多份;浏览器发现得晚,命中了也起不来。只要其中任意两项叠在一起,LCP 多出 500 毫秒以上 很常见;三项都存在时,页面从 2.3 秒掉到 3.3 秒 并不夸张。

首屏大图迟迟没出来

用户并不会分辨 “边缘未命中” 还是 “回源多走了一段路”。用户只会看到首屏大图晚了 300 毫秒、600 毫秒、1 秒。LCP 的良好目标仍是 2.5 秒以内,并且看的是 75 分位 页面访问;某一批用户持续慢,报表就会长期偏高。Google 的公开说明一直是按这个口径评估。

第一次访问最容易把问题放大。CDN 节点本地还没有那张 Hero 图,请求到达节点后,还要再去源站取一次文件。若源站在 Virginia,用户在 FrankfurtSydney,多出来的不是一次本地读取,而是一段跨区域网络往返,再加上源站处理时间。Cloudflare 说明里写得很清楚:默认会先遵循源站缓存头;没有合适的缓存条件或单独的 Edge TTL,资源就可能继续回源。

第一次访问慢,不只体现在“图出来得晚”。更常见的是:HTML 先到了,标题先显示,首屏图片位置空了 0.4–1.2 秒,用户还会误以为页面没加载完。Chrome 对 LCP 的拆解里,把这部分分成资源发现、资源请求、资源完成和元素渲染几个阶段;哪一段晚,LCP 就跟着晚。

  • 新访客访问广告页,节点里还没缓存首图,请求链路会多出 1 次 源站往返

  • 流量刚开始放量的前 10–30 分钟,很多 PoP 还处于冷缓存状态

  • 同一页面在本地办公网络里可能是 2.3 秒,换到跨洲访问后容易到 3 秒以上

  • 用户刷新第二次变快,不代表第一次的问题不存在;CrUX 和真实用户统计照样会记下第一次访问

  • 首屏图文件若有 500 KB、900 KB,MISS 场景下还会把下载时间叠上去

  • 边缘节点命中不稳定时,同一 URL 也可能上午快、下午慢,因为过期后又重新回源

读到这里,链路问题已经很清楚:第一次访问更慢,不是因为“第一次”这个词本身,而是因为缓存还没在离用户最近的节点里准备好。再往前一步,距离源站越远,差异越容易被看见。浏览器先连到就近 CDN 节点,看起来像是已经“全球分发”;但只要那张图不在节点本地,节点还要继续去源站取文件。用户虽然只发起了 1 次 请求,背后实际发生的是 2 段 网络传输。

跨区域访问时,多出来的几百毫秒不会平均消失。LCP 只关心首屏最大内容什么时候画完。对商品详情页、新闻首页、SaaS 落地页来说,这个元素通常就是大图。那张图晚到 400 毫秒,LCP 大致也会晚接近 400 毫秒。Google 在优化 LCP 的文档里也把重点放在两条请求上:初始 HTML 与 LCP 资源本身。

  • 用户在 London 访问美国东海岸源站,未命中时会比本地命中多一段跨洋往返

  • 用户在 Melbourne 打开欧洲站点,节点没图时,额外等待往往比同洲访问更明显

  • 同一批页面里,北美用户 LCP 2.4 秒,欧洲用户 2.9 秒,常见原因就是边缘覆盖和源站位置不同

  • 站点把源站集中在 1 个 区域时,越远的访问者越容易吃到回源成本

  • 首图更新后没做预热,新地区的前几批访问者通常都会经历一次冷启动

  • 页面其他静态文件都已命中,也不能抵消首屏主图的 MISS,因为 LCP 只认最大元素的完成时间

跨区域之后,移动网络会把等待继续放大。4G 或较差的 Wi-Fi 环境里,网络抖动、排队、丢包重传都比有线网络更明显;LCP 图如果还要多走回源链路,用户就更容易感到“图片慢半拍”。Google 与 Chrome 文档都把资源请求的开始时点看得很重,因为晚发起一次请求,在移动网络里往往不是多 50 毫秒,而是多出 数百毫秒

这里还有一个经常被忽略的点:图片已经缓存到边缘节点,也不代表用户一定会觉得快。浏览器必须先发现图片 URL,才能发请求。若首图写在 CSS background-image 里,浏览器通常要先下载并解析 CSS;若首图由轮播脚本插入,还要等 JS 初始化完成。也就是说,用户看到的慢,有时是一半来自 CDN 未命中,另一半来自浏览器发现得晚。

  • 首图写在 HTML <img> 中,浏览器能更早发现请求目标

  • 首图写在 CSS 背景里,往往要多等 1 个 CSS 请求与解析过程

  • 首图由 JS 再插入 DOM,请求开始时点会继续后移 数百毫秒

  • 给 LCP 图加 fetchpriority="high",浏览器会更早分配请求优先级

  • 若把首图错误地设成 loading="lazy",Chrome 文档明确提示应避免这样做

  • 预加载要指向首屏真正会显示的那一张图;预加载 5–10 张图片,优先级效果会被摊薄

假设 HTML 在 200 毫秒 到达,浏览器解析花 100 毫秒;首图藏在 CSS 背景里,又多等 1 个 样式请求;浏览器终于发现图片 URL,但边缘节点没有缓存,于是回源再多 300–800 毫秒;图片文件本身若有 700 KB,在 4G 环境里下载和解码还会继续往后拖。用户不会关心是哪一步出的问题,只会看到大图迟迟没出来。

再看真实报表层面,为什么某些流量入口会显得更差。Google 评估 LCP 看的是 75 分位,不是平均值。也就是说,只要广告流量、新访客、远距离访客、移动网络访客里,有足够多的一批人稳定慢 0.5–1.0 秒,整页就可能掉出良好区间。把局部问题归类为“偶发”没有实际帮助,因为报表不是按主观感觉算,而是按用户访问分布算。

  • 广告流量常带查询参数,可能让首图 URL 形成多个缓存版本

  • 新访客比例高的落地页,更容易长期出现冷缓存访问

  • 欧洲和北美同时投放时,远离源站的一侧常比另一侧慢 0.3–0.8 秒

  • 移动流量占比高于 50% 的页面,LCP 更容易受请求时机影响

  • 周末活动或新品发布后的前 1–2 小时,缓存尚未在所有区域变热,体验差异会更明显

  • 同一页面桌面端达标,移动端未达标,并不稀奇;Google 也建议移动与桌面分开观察 75 分位 数据

可以把“更明显地感觉到慢”理解成一组有规律的人群与场景:第一次访问、离源站更远、网络更弱、请求发现更晚。每项都能多出 数百毫秒,两项叠加后就可能从 2.4 秒3.2 秒。用户看到的只是首屏图迟到,报表记录的则是 LCP 变差;两边看到的是同一件事,只是语言不同。

启用 preload

很多站点把图片、字体、CSS 迁到 CDN 后,请求耗时从 400–800ms 降到 80–200ms,但 LCP 还是会多出 300–900ms。原因常见于浏览器太晚才发现首屏资源:HTML 先到,CSS 后解析,字体和首屏图再发请求。preload 的作用不是让资源“下载更快”,而是让它提前 1 个发现阶段进入请求队列。对首屏大图、首屏字体、首屏 CSS 来说,这一步常常比单纯换更快的 CDN 节点更。

什么资源该 preload

先看用户在页面打开后前 2.5 秒 内真正会看到什么,再决定 preload 谁。LCP 统计的是最大可见元素,不是文件体积最大,也不是请求数最多。很多页面首屏一共有 20 到 60 个请求,真正适合 preload 的通常只有 1 到 3 个。再多,浏览器前期带宽会被分掉;再少,LCP 资源又可能要等到 CSS、字体或脚本解析完才开始请求。

最常见的候选资源,通常落在这 3 类里:首屏主图、首屏字体、首屏必要 CSS。它们有一个共同点——用户滚动前就会看到,而且浏览器默认未必能很早发现。比如 Hero 图藏在 CSS 里,字体文件挂在 @font-face 后面,异步 CSS 又被拆到第二个样式表里。文件可能只有 30KB、90KB、180KB,但只要请求起晚了 200 到 600ms,LCP 就会跟着往后挪。

先处理图片。对很多 SaaS 官网、媒体首页、旅游站点、零售首页来说,LCP 元素最常见就是首屏图片。它可能是 <img>,也可能是 background-image。浏览器遇到普通 <img> 时,通常能在解析 HTML 的阶段较早发现;CSS 背景图要等 CSS 文件下载并解析完成后才知道地址。一个常见链路是:HTML 在 120ms 到达,CSS 在 260ms 开始,430ms 解析完,背景图到 450ms 才发请求。哪怕图片只要 180ms 就能下载完,LCP 也已经被前面的等待拖长了。

适合优先纳入图片 preload 的情况,可以先按下面看:

  • 首屏图占可视区面积 25% 以上
  • 图片宽度常见在 1200px 到 2000px
  • 资源大小在 80KB 到 250KB
  • 图片在用户滚动前就可见
  • DevTools 里该图片请求开始时间晚于 HTML 300ms 以上
  • 图片不是装饰性小图标,不是 logo,不是轮播第 2 张

如果页面首屏区域同时存在大图和大标题,先看谁是 LCP。Chrome Performance 或 Lighthouse 通常会给出 LCP 元素。如果它是 Hero 图,preload 图片的优先级通常高于 preload 字体。因为一张 160KB 的主图如果晚发起 400ms,能单独把 LCP 从 2.2s 推到 2.7s;而一个 32KB 的字体文件,更多影响的是文字呈现时机和布局稳定性,幅度常见小一些,但对文本首页也可能很明显。

图片资源里还要分几种情况。写法不同,适用性也不同。

页面情况是否常见适合 preload原因
HTML 里首屏 <img> 且位置靠前视情况浏览器本来就能较早发现
CSS 背景首图默认发现偏晚
JS 渲染后才插入的首屏图是,但要同时看渲染路径资源和元素出现都可能偏晚
轮播首张图且初始就可见视情况要确认确实是 LCP
轮播第 2 张以后用户初始看不到
缩略图、卡片图首屏面积通常不够大

再往下看字体。很多英文站点的首屏最大元素不是图片,而是大标题、价格文本或文章标题。比如新闻文章页,H1 常见字号 40px 到 64px;SaaS 首页主标题常是两行,每行宽度接近可视区的 60% 到 80%;产品页价格区在移动端会占据屏幕上半部分。这个时候字体文件的请求时机很容易影响 LCP。字体通常要等 CSS 下载、解析、构建样式后,浏览器才知道要不要去拿 woff2。如果字体请求在 500ms 才开始,而文本本来是首屏主体,LCP 就会跟着晚。

适合考虑 preload 字体的页面,常见特征有:

  • LCP 元素是文本,不是图片
  • 首屏大标题使用自定义 Web Font
  • 字体文件是 woff2,大小在 20KB 到 60KB
  • 该字体仅对应 1 到 2 个首屏字重
  • 字体请求比 HTML 晚 200 到 500ms
  • 回退字体与目标字体字宽差异较大,容易触发布局变化

这里要注意“只 preload 真正首屏会用到的字重”。不少页面引入 300/400/500/700 四个字重,总体积 120KB 到 240KB,但首屏只用到了一个 700 的标题和一个 400 的正文。把四个字重都提前拉下来,浏览器前 1 秒 的请求数会增加 2 到 4 个,收益通常不如只预加载 700。如果首屏正文可以先用系统字体,等正文以下再慢慢补齐别的字重,用户几乎不会注意到。

字体适不适合 preload,可以用这个顺序判断:

  1. 首屏最大元素是不是文本
  2. 这个文本是否依赖自定义字体
  3. 该字体是否在首屏立即用到
  4. 只 preload 1 到 2 个文件能否覆盖首屏文字
  5. 是否跨域,是否需要 crossorigin

一旦跨域字体进入候选,HTML 里通常要写成下面这样:

<link rel="preload" as="font" href="https://static.example.com/fonts/inter-700.woff2" type="font/woff2" crossorigin>

少了 crossorigin,预加载与实际使用之间可能无法完全复用。单个字体虽然只有 30KB 左右,但首屏文本型页面里,晚 150ms 到 300ms 已经足够让 LCP 从 “良好” 跨到 “需要改进”。

图片和字体之外,第三类才轮到首屏必要 CSS。并不是所有 CSS 都适合 preload。一个完整站点的 CSS 可能有 80KB 到 300KB,还常常分成 base.csslayout.csscomponents.csshome.csstheme.css。浏览器本来就会把 <link rel="stylesheet"> 看得很重要,所以“普通主样式表”不一定需要再 preload 一遍。真正值得考虑的是:那份样式在现有加载链路里出现得偏晚,而且它控制的是首屏布局、首屏图片尺寸、首屏文本样式或首屏可见性。

下面几种 CSS 更接近 preload 候选:

  • 首屏专用的 above-the-fold.css
  • 首页模板的首屏布局样式
  • 控制首图容器高度、比例、显示状态的样式
  • 控制首屏标题字体映射的样式
  • 被异步拆分,结果首屏仍依赖它的样式文件

如果一份 CSS 在首屏阶段不参与布局和渲染,比如只管页脚、二级 tabs、用户评论、推荐位,那它即使大小只有 18KB,也不值得抢占前期请求位置。因为浏览器在首 700ms 到 1200ms 能处理的高优先级资源是有限的,多一个,就会让别的资源排队更久一点。

可以把 CSS 的判断压缩成这张表:

CSS 类型是否常见适合 preload说明
主样式表,本来就在 <head>很少浏览器本身已高优先级处理
首屏拆分 CSS,加载较晚适合提前发现
页面交互样式首屏渲染前不必优先
页脚、评论、推荐位样式用户初始不可见
控制首图尺寸和可见性的 CSS会影响 LCP 元素呈现

接下来要防止一个很常见的误判:把“文件越大”当成“越该 preload”。实际不成立。一个 300KB 的视频播放器脚本,虽然大,但用户进入页面前 2 秒 未必需要;一张 140KB 的 Hero 图,虽然不大,但它是 LCP 元素,就更适合先拿。判断 preload 资格,不看体积排名,看的是“用户第一个屏幕会不会马上用到”。文件体积只是第二层条件,因为太大的资源即使提前发起,也可能把前期带宽塞满。

可以用一组更实用的筛选标准,把候选资源从几十个缩到 3 个以内:

  • 用户滚动前是否可见
  • 是否参与 LCP 元素渲染
  • 浏览器默认发现是否偏晚
  • 资源体积是否能控制在 250KB
  • 资源数量是否能控制在 1 到 3
  • 是否会因为 preload 触发重复下载

如果一个资源同时满足前 3 项,基本就进入候选;如果前 3 项满足,但体积达到 500KB 以上,就要结合格式压缩、分辨率和设备策略一起看;如果一个页面已经预加载了 4 到 6 个资源,就该开始删,而不是再加。

还有一个页面类型差异不能忽略。首页、文章页、产品页、登录页的 preload 对象往往不是同一批。新闻文章页的 LCP 常见是标题或封面图;产品详情页常是主商品图,尺寸常在 1200px 到 1800px;登录页有时反而是左侧宣传图或品牌标题;文档页很多时候没有大图,LCP 可能就是首段标题文本。把同一套 preload 规则硬套到所有模板,命中率通常会下降。按页面类型做差异化配置,更容易把预加载数量压在 1 到 2 个,同时把命中率维持在较高水平。

这里可以先按模板粗分:

  • 首页:Hero 图、首屏标题字体、首屏布局 CSS
  • 文章页:封面图或标题字体
  • 产品页:主商品图、价格区字体
  • 登录页:品牌图或宣传图
  • 文档页:标题字体,少量首屏样式

把 preload 理解成“给少数首屏资源发一张更早入场的通行证”会更好操作。页面里首屏真正马上要用的东西本来就不多,很多站点最终稳定运行时,只有 1 张图 + 1 个字体,或者 1 张图 + 1 份首屏 CSS。数量控制住,资源选对,用户通常会更早看到完整首屏,而不是更早下载一堆暂时还看不到的文件。

怎么写、怎么测

先写对,再去测。很多页面的问题不是没写 preload,而是标签位置、资源类型、跨域参数、实际命中的文件名没有对上。浏览器在解析 <head> 的前 100 到 300ms 内,会决定前一批高优先级请求的排序;如果 preload 出现在很靠后的位置,或者由脚本晚一点注入,效果会明显变差。把标签放进原始 HTML 的 <head> 前半段,通常比通过客户端 JS 在 500ms 之后再插入更有用。

先看写法。图片、字体、样式表的属性不能混用,少一个字段都可能影响复用和优先级。一个页面里常见只需要写 1 到 3preload,超过 4 个就要开始检查有没有挤占首屏带宽。下面这组写法更稳,适合首屏 Hero 图、首屏字体、首屏必要 CSS:

<link rel="preload" as="image" href="/images/hero-1280.webp">

<link rel="preload" as="font" href="https://static.example.com/fonts/inter-700.woff2" type="font/woff2" crossorigin>

<link rel="preload" as="style" href="/css/above-the-fold.css">
<link rel="stylesheet" href="/css/above-the-fold.css">

图片写法里,as="image" 不能省。浏览器会根据 as 判断请求优先级、内容类型和缓存复用方式。字体除了 as="font",还要有 type="font/woff2"crossorigin。不少站点把字体放在独立静态域名,没写 crossorigin 时,DevTools 里会看到预加载请求和真实使用请求没有完全合并,首屏标题会多等 100 到 250ms。样式表则要记住一点:preload 只负责提前下载,真正让样式生效的仍然是 <link rel="stylesheet">

如果站点用了响应式图片,写法还要再往前走一步。桌面端和移动端常常不是同一张图,桌面图可能 240KB,移动图只有 95KB。模板里固定 preload 桌面图,在手机上常见会白下 100 到 200KB。更合适的做法是让预加载和实际设备条件对齐,至少保证 <picture>srcset、断点逻辑与首屏请求一致。你在 DevTools 网络面板里如果看到首屏图被请求了两次,一次 hero-desktop.webp,一次 hero-mobile.webp,基本就要先修这个问题,再谈别的优化。

下面这张表适合拿来对照 HTML:

资源必填属性常见遗漏结果
图片rel="preload" as="image" hrefas优先级和复用可能不理想
字体as="font" type="font/woff2" crossorigincrossorigin预加载与实际使用可能不复用
CSSas="style" + stylesheet只写 preload 不写 stylesheet文件下载了但样式不生效
跨域图片as="image",必要时配 preconnect少连接预热DNS/TCP/TLS 仍会拖 80 到 300ms

写完以后,不要先盯着 Lighthouse 分数,而是先看 3 个更具体的量:LCP 资源开始请求的时间、LCP 元素真正渲染出来的时间、首屏阶段额外增加了多少请求和体积。Lighthouse 是综合结果,适合最后看;排查时,Chrome DevTools、WebPageTest、真实用户监测里的时间线更好用。一个常见目标是:让首屏 LCP 图或 LCP 字体的请求开始时间比原来提前 150 到 500ms。如果只提前了 30ms,体感和分数通常都不明显。

可以先按这个顺序测:

  1. 打开 DevTools 的 Network 和 Performance
  2. 清缓存,勾选 Disable cache
  3. 模拟 Slow 4G 或普通 4G,CPU 降速 4x
  4. 刷新页面,记录 HTML、CSS、LCP 资源开始时间
  5. 再启用 preload,重复同一条件再测 3 次

对比时不要只看单次结果。移动网络下单次波动 100 到 300ms 很常见,尤其是 TLS 握手和首包到达。连续测 3 到 5 次,取中位数更接近真实情况。比如原始版本首屏图在 780ms / 810ms / 730ms 开始请求,启用 preload 后变成 410ms / 450ms / 430ms,就能判断请求确实前移了约 320 到 370ms。再去看 LCP 有没有从 2.9s 稳定降到 2.4s 左右,结论会更清楚。

很多人会忽略浏览器控制台里的警告,但那里面经常有线索。Chrome 常见会提示 “resource was preloaded but not used within a few seconds from the window’s load event”。出现这句,通常不是浏览器“误会了你”,而是页面预加载了一个首屏根本没用到的文件,或者文件名、资源类型、设备条件没有对上。假如一个页面预加载了 3 张轮播图,首屏只用到第 1 张,后两张常常就会触发类似提示。继续保留,只会让首屏前 1 秒 的请求数多出 2 个。

排查时可以优先看这几项:

  • Console 是否出现未使用的 preload 提示
  • Network 里是否有同类资源重复请求
  • 首屏图请求优先级是否升到 High
  • LCP 资源的 Start Time 是否明显提前
  • HTML 体积是否因为内联配置过多上涨了 5KB 到 20KB

如果用了跨域 CDN,还要看连接准备时间。一个站点把 HTML 放在主域、图片放在 img.examplecdn.com、字体放在 fonts.examplecdn.com 时,浏览器前期可能要准备 2 到 3 个额外连接。桌面端一个新连接常见 80 到 180ms,移动端 150 到 400ms

这时只有 preload 还不够,通常要一起评估 preconnect。否则浏览器会更早知道首屏图地址,但还是要先走 DNS、TCP、TLS,时间线里看起来像“请求提早了,但卡在 Initial connection”。

<link rel="preconnect" href="https://img.examplecdn.com" crossorigin>
<link rel="preload" as="image" href="https://img.examplecdn.com/hero.webp">

这两行经常一起出现,尤其是首屏图来自独立图片 CDN。测的时候别只看总耗时,要拆成 Queueing、Stalled、DNS、Initial connection、Request sent、Waiting (TTFB)、Content download。若 Initial connection 单项就占了 220ms,先把连接准备好,比继续多加一个 preload 更有帮助。

再往下就要防止“写了反而更慢”。最常见的情况是预加载数量太多。浏览器前期可同时推进的高优先级资源是有限的,页面在前 800ms 内如果就出现 6 到 10 个高优先级请求,首屏图和首屏字体就要排队。

你在网络瀑布图里会看到队头拥挤、请求错峰启动、下载片段被切得很碎。一个页面原本只有 HTML、CSS、首屏图、字体 4 个早期请求,后来又加进第 2 张轮播图、第 3 张轮播图、视频封面、图标字体和聊天脚本,LCP 变差 200 到 600ms 并不稀奇。

下面这组情况最容易把优化做反:

写法常见后果量化表现
预加载 5 张以上图片首屏带宽分散LCP 图开始时间反而晚 100 到 300ms
同时预加载多个字重字体拥挤首屏文本未必更早,前期多出 40 到 150KB
预加载非首屏 CSS/JS抢前期连接首屏请求队列更长
预加载错误文件名无效下载额外浪费 50 到 300KB
首屏图仍保留 loading="lazy"请求调度冲突图片开始时间常晚 200 到 500ms

还有一种容易被忽略:资源提前到了,但渲染没提前。比如首屏图在 450ms 就下载完,页面却由大型前端框架在客户端渲染,主线程从 500ms 忙到 1300ms,图片节点直到 1.4s 才插入 DOM。网络面板看上去不错,LCP 仍然差。这里该看的不是 preload 标签,而是脚本执行时间、长任务数量、首屏组件何时挂载。Performance 面板里如果 Long Task 连续出现 3 到 5 段,每段 80ms 到 200ms,预加载再多也帮不上太多。

一个常见场景:首屏 Hero 图大小 170KB,preload 后在 380ms 下载完成;React 或 Vue 首屏模块在 1.1s 才完成 hydration;最终 LCP 仍停在 2.6s。网络不是问题,元素出现得晚才是问题。

因此测的时候最好把网络和渲染一起看。LCP 改善比较明显的页面,通常会同时出现两件事:LCP 资源开始时间提前 200 到 500ms,LCP 元素渲染时间也同步前移 150 到 400ms。如果只发生前者,没有后者,说明还卡在渲染链路。这个区分很有用,能避免把时间继续花在 <head> 里加新标签。

可以把验证结果整理成一张对照表,方便一次看明白:

指标预期变化如果没变化,优先排查
LCP 资源 Start Time提前 150 到 500ms标签位置、文件名、跨域参数
Priority变成 High 或更早进队列as 是否正确
Initial connection降低 80 到 250ms是否缺 preconnect
LCP Render Time提前 100 到 400msJS 执行、hydration、DOM 插入时机
Console 警告减少未使用 preload资源是否命中首屏
首屏总请求数控制在较少范围是否预加载过多

还有版本一致性。构建后文件名带哈希的站点很常见,hero.a1b2c3.webp 每次部署都可能变。模板中的 preload 一旦没跟着更新,浏览器就会先下旧图,再下新图。CSS 文件也一样,尤其是拆包后的 home.9f8e7d.csscritical.1a2b3c.css

部署完成后最好做一次真实页面抓取,确认 HTML 中的 preload URL 和 Network 面板里的实际请求 URL 完全一致。只要有一位字符不同,首屏阶段就可能多出 1 个请求 + 80KB 到 250KB 的流量

优化 DNS 解析

首屏资源每多一个新域名,浏览器通常就要多做一次 DNS 查询、一次连接建立、一次 TLS 握手。桌面网络下,单个域名首次解析常见增加 20–80ms,移动网络常见增加 50–150ms。如果 LCP 图片、字体、CSS 分散在 3 个独立主机名,首屏请求启动时间可能多出 150–400ms。优化方式很明确:把首屏资源收敛到 1–2 个域名,只给首屏必需域名加 preconnectdns-prefetch,减少无关第三方域名在首屏阶段出现。

减少首屏域名

很多页面用了 CDN 以后,资源下载时间看起来变短了,但 LCP 还是没有改善,常见原因不是文件太大,而是浏览器在首屏阶段遇到了太多新域名。用户第一次打开页面时,浏览器拿到 HTML 后,要继续发现 CSS、字体、图片、脚本分别来自哪里。

只要首屏里多出 2 到 4 个新主机名,浏览器就会多做几轮 DNS 查询、TCP 建连和 TLS 握手。桌面网络里,单个新域名首次准备常见会增加 60–180ms;移动网络下,合计到 100–300ms 并不少见。LCP 图片如果恰好落在最后才出现的新域名上,下载启动时间就会被整体推后。

先看一个很常见的首页资源分布。

主站 HTML 在 www.example.com,首屏图片在 img.examplecdn.com,CSS 在 static.examplecdn.com,字体在 fonts.example.net,埋点脚本在 analytics.vendor.com。从文件管理角度看很清楚,但从浏览器前 500ms 的工作顺序看,事情就变多了。浏览器不会因为这些资源“都属于同一个品牌站点”就少做准备。每个主机名都要单独处理。对一个目标是把 LCP 压到 2.5 秒以内 的页面来说,前面平白多出 150–400ms,影响已经很明显。

很多团队会先压图片体积,比如把首屏图从 260KB 压到 180KB,或者把 CSS 从 90KB 精简到 65KB。文件变小当然有用,但如果 LCP 图片的请求起点已经晚了 300ms,这类压缩带来的改善往往不如减少首屏域名数量。因为用户看到的是“首屏最大内容什么时候开始出现”,而不是资源在传输过程中节省了多少 KB。只要请求起点推迟,下载再快,也是在晚一点的时间线上开始。

可以先把首屏真正参与渲染的资源列出来,再看它们分布在哪些主机名上。大多数首页的上半屏,通常只会依赖下面几类内容:

  • HTML 文档本身

  • 1 张 LCP 图片,常见为 120KB–350KB

  • 1 份首屏 CSS,常见为 20KB–80KB

  • 1 套首屏字体,若用了外部字体

  • 少量立即执行的脚本,常见为导航、折叠、轮播控制

如果这些文件被拆到 4 个以上域名,浏览器前期准备时间通常会明显增加。如果它们集中在 1 到 2 个域名,首屏请求流程会短很多。

下面这个对比更容易看清问题:

首屏资源放置方式首屏新域名数常见前期准备耗时LCP 请求启动情况
HTML、图片、CSS 基本在同域名140–120ms较早
图片、CSS、字体各自独立3150–320ms较晚
再叠加监测、客服、社媒组件5–7250–600ms更晚

首屏阶段先减少主机名数量,通常比继续拆分资源域名更容易让 LCP 提前。

浏览器对“新域名”的反应不是抽象概念,而是具体的时序工作。每多一个新主机名,往往会多一轮 DNS 和连接准备。若页面上方只有一张主视觉图、一行标题、一段说明文字,却在首 600ms 内遇到 5 个外部域名,那首屏加载顺序大概率已经不够紧凑。很多时候,页面并不是“下载太慢”,而是“请求开始得太晚”。

更实用的做法,是先按“用户一进入页面马上能看到什么”来收拢资源。例如首屏只有 Hero 图和按钮,那么更合理的组合通常是:

  • HTML 与 CSS 在主站或同一个静态资源域名

  • LCP 图片与 CSS 尽量共用同一个 CDN 域名

  • 字体若不是视觉上必须,优先使用系统字体

  • 首屏后才会出现的脚本,不放进前 1 秒 的请求里

这一组调整做完后,首页首屏常常能从 4 个主机名 收敛到 2 个。对不少企业站、内容站、服务站点来说,光这一项就能让 LCP 请求提前 80–220ms

再往下看,很多页面把资源拆得过细,不是因为技术上必须,而是沿用了旧的资产管理方式。例如:

  • 图片走 img 子域名

  • CSS 走 static 子域名

  • JS 走 js 子域名

  • 字体走 fonts 子域名

  • 视频缩略图走另一个媒体域名

这样的分法在 HTTP/1.1 年代还常见,用来分散连接数压力;但现在很多页面已经跑在 HTTP/2 或 HTTP/3 上,继续拆成过多主机名,收益往往不高,浏览器反而要为每个域名付出额外准备时间。如果服务器与 CDN 配置允许,把首屏 CSS、图片、部分 JS 合并到同一个静态域名,通常更合适。

下面这组场景,比较容易判断是否应该合并域名:

现象更适合的处理
首屏只有 1 张大图,却来自独立图片域名与首屏 CSS 合并到同一静态域名
字体只用于标题,但放在第三方字体服务改为本地托管或系统字体
首屏 JS 很少,却使用专门脚本子域名与静态资源域名合并
CSS、图片、SVG 图标分别来自 3 个域名收拢为 1–2 个域名

有些页面把字体服务也算进首屏域名里,结果问题会更明显。外部字体通常不只带来一个域名。有时字体 CSS 在 fonts.provider.com,真实字体文件又去 static.providercdn.com,浏览器等于要处理 2 个域名。若页面上半屏文本量不大,或者系统字体替代效果可接受,去掉外部字体服务,常见能减少 1–2 次连接准备

在首屏以图片和按钮为主的页面里,用户通常更容易感知到主视觉图晚出现 200ms,而不是标题换成系统字体带来的细微差别。

继续看第三方资源。很多首页在首屏阶段同时引入:

  • 分析脚本

  • 会话录制工具

  • 客服组件

  • 社媒按钮

  • 视频平台 SDK

  • A/B 测试脚本

这些内容各自带来 1 个或多个外部主机名。即使它们不参与 LCP,也会挤占浏览器前期资源。如果首屏前 800ms 内冒出 4–6 个第三方域名,LCP 图片的优先级通常会被拉低。这类页面更适合先做“域名减法”,而不是先做“资源微调”。

可以按下面一组标准快速判断页面是否需要先减域名:

  • 首屏可见区域内,资源是否分散在 3 个以上新域名

  • LCP 图片是否来自第一次出现的新主机名

  • 字体是否来自第三方,且字体文件还在另一个 CDN 域名

  • 首屏前 500ms 内是否就出现客服、分析、社媒等域名

  • 首屏 CSS 与图片是否落在不同 CDN 主机名上

如果其中有 3 项以上 是“是”,优先做域名收拢会比继续压图片更划算。因为在这种页面里,LCP 往往输在前期准备,而不是输在传输体积。有时页面首屏资源已经不多,但仍然用了多个业务子域名。例如:

  • www.example.com 提供 HTML

  • assets.example.com 提供 CSS

  • media.example.com 提供图片

  • fonts.example.com 提供字体

这种结构在管理上容易区分,但浏览器并不会因为都在 example.com 下面就省略 DNS、TCP、TLS。从网络层看,它们仍然是不同主机名。把 assetsmedia 合并成一个 static 域名后,首屏前期通常就少掉一轮完整准备过程。若用户访问 RTT 在 80ms 左右,这轮准备常常就值 100ms 以上

还要考虑缓存命中后的现实差异。有些人会说,用户第二次访问时 DNS 和连接都能复用,域名多一点没关系。但首屏性能评估通常更看重首次访问、弱网访问、跨区域访问、无缓存访问。实用户数据里,首次进入首页、广告落地页、搜索结果页点击进入的新访客比例,常常不低于 30%–70%。如果页面把速度建立在“用户已经缓存过多个域名”的前提上,首页体验会比较脆弱。

下面给一个更容易执行的整理方式。把资源按“首屏必需”和“首屏后再说”分开,再对应到域名:

资源是否首屏必需更合理的放置
LCP 图片与首屏 CSS 同域名
首屏 CSS主站或同一静态域名
首屏字体视页面而定本地托管或系统字体
分析脚本延后加载
客服组件用户停留后再加载
社媒/视频脚本首屏后再请求

这一步做完后,首屏请求链通常会短很多。浏览器拿到 HTML 后,能更快发现首屏资源,也不需要为过多新域名做准备。
不少页面从 5 个首屏主机名 缩到 2 个 以后,LCP 图片发起时间能从 550ms 提前到 300ms 左右。如果图片本身体积已经不大,这一段时序变化往往比继续压缩格式更明显。

也可以结合瀑布图去验证。若页面里 HTML 在 200ms 左右返回,LCP 图片只有 180KB,却到 600ms 才开始请求,那多半不是图片下载慢。如果在这 400ms 之间,能看到 DNS Lookup、Initial connection、SSL,同时前面还夹着多个新域名请求,说明首屏域名数量已经影响到了资源启动顺序。删掉不必要的首屏域名后,再看 LCP 图片是否提前到 250–400ms 发起,变化会很直观。

对首屏来说,资源放在哪个域名上,往往比资源归属哪个业务模块更影响加载顺序。

当页面完成这一轮收拢后,再去看 preconnect、图片压缩、字体裁剪、缓存策略,顺序会更合理。因为浏览器前期准备工作已经变少,后续每一项优化才更容易体现到真实用户的首屏时间里。

只给首屏必需域名做 dns-prefetch 和 preconnect

很多页面接入 CDN 后,LCP 变慢,不是资源下载慢,而是浏览器在资源真正发起请求前,多花了时间做域名解析、TCP 建连和 TLS 握手。首屏里只要出现 2 到 4 个首次访问的新域名,浏览器前几百毫秒的准备工作就会变多。常见桌面网络下,单个新域名的 DNS 查询约 20–80ms,TCP+TLS 再加 40–150ms;移动网络下,这一段更容易到 100–300ms。所以 dns-prefetchpreconnect 不是“多写几个更保险”,而是要只给首屏马上会用到的域名,顺序排错了,反而会让浏览器前期更忙。

先区分两个标签。dns-prefetch 只提前做 DNS 解析,浏览器知道这个域名后,能先把 IP 查好;等资源真正要加载时,再建立连接。preconnect 会更进一步,先把 DNS、TCP、TLS 一起做掉。两者的成本不同,收益也不同。

如果某个域名承载的是 LCP 图片、首屏字体、首屏接口preconnect 更适合;如果只是首屏后才可能用到的服务,例如评论、客服、热图、推荐模块,通常 dns-prefetch 已经够了,甚至可以不加。

代码层面的写法很简单,但页面里只放“马上会用到”的那几个:

<link rel="dns-prefetch" href="//cdn.example.com">
<link rel="preconnect" href="https://cdn.example.com" crossorigin>

浏览器前期能分配的连接资源有限。很多首页为了“保险”,在 <head> 里一次性写入 6 到 10 个 preconnect,把图片 CDN、字体服务、分析脚本、客服系统、A/B 测试、广告网络都提前连接。结果是浏览器前 300–800ms 里先忙着给一堆域名建连,真正参与首屏渲染的域名并没有更快。对于首屏只有 1 张 LCP 图、1 份 CSS、少量文本的页面,preconnect 数量通常控制在 1–3 个 更合适。

可以先把首页资源按“首屏是否马上依赖”分两组看:

资源类型是否首屏立刻需要更适合的提示方式常见数量
LCP 图片 CDNpreconnect1
首屏字体域名视页面而定preconnect 或不加0–1
首屏接口域名视是否阻塞渲染而定preconnect0–1
评论系统dns-prefetch 或不加0–1
客服/聊天工具不加或延后0–1
分析/热图/A/B 工具dns-prefetch 或延后1–3

这样做的原因很简单。浏览器从拿到 HTML 到发起 LCP 资源请求,中间只有很短的一段窗口。若页面在最开始同时为 5 个以上域名 建连,浏览器调度会更分散。对用户而言,看到的是首屏大图或首段内容晚出现了 100–300ms,并不会因为评论系统提前准备好了而感觉页面更快。

判断谁属于“首屏必需域名”,不能只看业务上是否重要,要看它是否参与首屏渲染。举个常见场景:首页首屏包含一张 Hero 图片、一行标题、一段副标题、一个按钮。那么浏览器优先会用到的通常是:

  • 主站 HTML 域名
  • LCP 图片所在域名
  • 首屏 CSS 所在域名
  • 若首屏文字用了外部字体,再加字体域名

如果 CSS 和图片都已经在同一个 CDN 域名,首屏阶段很多时候只需要 1 个 preconnect。如果字体是本地托管,甚至可以只给图片 CDN 域名预连接,页面前期准备会更轻。

下面是一个更接近真实页面的对比:

页面写法head 中 preconnect 数量前 500ms 新域名数LCP 请求发起时间常见区间
只连图片 CDN + 首屏字体22–3200–450ms
图片、字体、分析、客服、广告都连65–7400–800ms

很多站点接入了 Google Fonts、YouTube 视频、HubSpot、Hotjar、Intercom、Meta Pixel、A/B testing 工具后,头部会很热闹。每个服务都来自不同域名,浏览器头几百毫秒就在做 DNS、TCP、TLS。只要首屏渲染本身并不依赖这些域名,把它们放到后面,通常比“全部预连接”更有利。

例如客服浮窗平均在用户进入页面后 3–10 秒 才会被注意到,让它在页面首 300ms 内抢连接,没有多少意义。

还要注意 preconnect 的副作用。它会提前建立连接,占用浏览器套接字和网络资源。如果某个连接建好后很久都不用,浏览器有时会关闭它,或者这段准备工作本身就变成了无效开销。例如某页面对字体服务做了 preconnect,但首屏文字最终使用的是系统字体回退,真实字体文件在 2 秒后 才请求,那么前面的连接准备就没有多少收益。同样,如果某接口要等用户滚动或点击后才触发,也不该和 LCP 图片放在同一优先级。

更实用的排法,可以参考下面这一组判断:

  • LCP 图片是否在 HTML 初始内容里就能被发现
  • 首屏 CSS 是否阻塞首屏布局
  • 字体是否会改变首屏文字尺寸或换行
  • 接口数据是否影响首屏主区域内容展示
  • 第三方脚本是否会在首屏阶段真正输出用户可见内容

只有前 4 项回答为“会”的域名,才更接近 preconnect 的使用范围。如果一个域名只负责埋点、统计、会话录制,它对首屏渲染通常不构成硬依赖。

可以把优先级排成一个小清单:

  1. LCP 图片域名
  2. 首屏 CSS 或阻塞式样式资源域名
  3. 首屏字体域名(只有在首屏视觉依赖它时才排前面)
  4. 首屏接口域名(只有在 HTML 本身拿不到内容时才保留)
  5. 其余域名延后处理

这样处理后,浏览器在前 1 秒 里的事情会更单纯。例如原本首页有 7 个 preconnect,其中只有图片 CDN 真参与首屏,删到 2 个 后,LCP 请求起点常见能提前 80–200ms。如果原页面还叠加了移动网络和高 RTT 环境,提前幅度有时会更大。

再看 dns-prefetch。它的成本比 preconnect 低,不会像 preconnect 那样把整条连接都建好,所以适合给“很快会访问,但不阻塞首屏”的域名。比如页面底部的推荐内容接口、评论域名、视频平台、次要图片域名,都可以考虑只做 DNS 预取。单看收益,dns-prefetch 往往只能收回 10–50ms 的解析时间;但如果首屏后半段会立刻请求该域名,这点时间仍然有用。

对不参与首屏的第三方服务,也可以完全不写,等浏览器按正常节奏去请求。

下面这个表更容易落地:

场景建议原因
LCP 图来自 cdn.example.compreconnect首屏马上请求,能省 DNS+TCP+TLS
首屏正文用外部字体,且无系统字体替代preconnect可减少文字稳定前的等待
页面 2 秒后才加载客服不加或延后脚本首屏阶段看不到它
底部视频来自 youtube-nocookie.comdns-prefetch 或不加不阻塞上半屏显示
埋点、热图、A/B 测试工具dns-prefetch 或延后对首屏可见区域无依赖

有些团队会问:既然 preconnect 收益更大,为什么不全部用它?原因在浏览器调度上。前期一旦出现太多高优先级连接,真正首屏需要的资源不一定更快,反而会让线程和网络资源更分散。对于首屏只有 1 个主视觉图、1 个样式文件、1 套字体 的页面,头部保留 2 个 preconnect 往往已经够了。超过 3 个,就应该重新看哪个域名其实可以降级到 dns-prefetch,或者根本后移。

还要结合字体策略一起看。如果页面使用外部字体,而字体本身体积有 40KB、80KB、120KB 三档,且包含多个字重,浏览器不仅要预连接字体域名,还要下载真实文件。
在这种情况下,更常见的优化不是多写一个 preconnect,而是:

  • 只保留首屏需要的 1–2 个字重
  • 使用 font-display: swap
  • 优先本地托管字体文件
  • 避免字体 CSS 再跳到另一个新域名取文件

这样首屏文字出现得更稳定,也不会为了一个字体域名消耗过多前期准备时间。若首屏主要是图片和少量按钮,很多时候用系统字体就够了,连字体域名的 preconnect 都省掉。

检查是否写对,可以从瀑布图里看两个地方。一是 LCP 图片请求之前,是否已经提前完成了图片域名的 DNS 和连接;二是页面最开始是否出现了很多“早建好但暂时没用”的连接。如果你看到客服、广告、分析工具的连接都在 0–300ms 内建好了,而 LCP 图片要到 500ms 才开始请求,优先级就排错了。

如果删掉多余 preconnect 后,LCP 图片请求提前到 250–400ms,说明浏览器前期资源调度变轻了。

缩短 DNS 解析链路

很多页面的 LCP 变慢,不是 CDN 节点下载慢,而是资源真正发起请求之前,浏览器先在 DNS 上多花了时间。用户首次打开页面时,浏览器要先解析 HTML,再发现图片、CSS、字体、脚本分别来自哪些主机名。只要首屏资源落在 2 到 4 个新域名 上,浏览器就会增加多轮 DNS 查询。桌面网络里,单个域名首次解析常见在 20–80ms;4G 或跨区域移动网络里,常见会到 60–150ms。如果 LCP 图片域名本身还带多层 CNAME,等待时间继续往上叠,首屏最大元素就会晚一截出现。

很多站点接入 CDN 后,会把资源拆成 img.example.comstatic.example.comfonts.example.comjs.example.com。从资源管理角度看很清楚,但从浏览器时序看,首屏准备阶段就变长了。浏览器不会因为这些域名都“属于同一站”就复用 DNS 结果。每个新主机名都要单独查一次。

假设首页首屏依赖 3 个第一次出现的域名,每个域名多消耗 50ms,再加上连接建立和 TLS,LCP 资源开始下载的时间就可能被推迟 150–300ms。如果页面本来目标是把 LCP 压到 2.5 秒以内,前面多出来的几百毫秒已经足够影响评分。

再往下看,问题通常不止“域名多”,还包括“解析链路长”。有些资源域名不是一跳就到边缘节点,而是先到业务域名,再到接入域名,再到厂商调度域名,最后才落到实际节点。浏览器看到的是一个域名,背后实际走了 2 到 4 层 CNAME。解析链越长,首次访问耗时越不稳定。

用户在 North America、Western Europe、Middle East 等不同地区访问时,DNS 响应差异常会拉到 30–120ms。如果本地网络质量一般,差值还会更大,页面表现就会出现“有时快、有时慢”的情况。

先看资源主机名数量。对首屏来说,不是全站总请求数最先影响 LCP,而是首屏阶段新出现多少主机名。LCP 图片、首屏 CSS、首屏字体,尽量收敛到 1 到 2 个域名,浏览器前期准备会轻很多。很多企业站、内容站、服务站点把图片、样式、字体拆到 4 个域名,下载过程未必更快,但请求起点更晚。把图片和 CSS 合并到同一个 CDN 域名后,常见能收回 80–200ms 的前置等待。

可以先按下面顺序检查首屏依赖:

  • HTML 来自哪个域名,是否与主站一致
  • LCP 图片是否放在首次出现的新主机名
  • 首屏 CSS 是否与图片分属不同域名
  • 字体文件是否依赖单独字体域名
  • 首屏脚本是否又引入 2 个以上第三方主机名

如果首页头部里,浏览器前 500ms 内已经遇到 4 个以上新域名,首屏通常不会太轻。如果首屏最大图片在瀑布图里 400ms 之后才开始请求,优先看 DNS 和连接准备,不要先看图片体积。

继续往下查时,要把注意力放到 CNAME 层数和 DNS 提供商响应。很多团队只看 CDN 厂商面板里的边缘命中率,却不看 DNS 解析链长度。命中率 95% 以上,不代表首屏请求一定早。若 img.example.com 先 CNAME 到 img.cdn.vendor.net,再跳到 geo.vendor.net,最后再到实际边缘记录,解析阶段就会多几步。不同公共解析器返回时间也不同,常见差距能到 20–70ms。对于图片较大的页面,这部分时间看起来不夸张;但如果 LCP 图片本身只有 120KB–250KB,前面的 DNS 延迟占比就很明显。

更稳妥的做法,是把首屏资源域名的解析链尽量压短:

  • 首屏图片域名尽量控制在 1 到 2 层 CNAME
  • 不把字体再拆到第三方字体平台,能本地托管就本地托管
  • 不为 CSS、图片、脚本各自单独分配主机名
  • 减少“业务子域名 → 接入层 → 调度层 → 区域层”的多跳结构
  • 对首屏资源,优先选全球解析表现更稳定的 DNS 服务商

如果用户分布比较分散,比如 40% 在 United States,30% 在 Europe,20% 在 Southeast Asia,其余在 Oceania 和 Middle East,就更要看全球 DNS 响应是否均衡。某些服务商在单一区域解析很快,但在跨洲访问时,首查时间会高出 50ms 以上。页面测速只在一个地区做,容易把问题漏掉。

当域名数量控制住后,下一步是给真正会参与首屏渲染的域名做预连接。这里要区分 dns-prefetchpreconnect。前者只提前做 DNS,后者连 TCP 与 TLS 也一起准备。首屏最大图所在域名,如果一定要单独存在,更适合加 preconnect

如果只是评论系统、客服插件、热图脚本,通常不必占用首屏连接资源。首页 head 里保留 1 到 3 个 preconnect 比较稳,写到 5 个以上,浏览器往往会把前期连接资源分散掉。

例如首页首屏需要:

  • 1 个主站域名
  • 1 个图片 CDN 域名
  • 1 个字体域名

那优先级通常应该是:

  • 图片 CDN:preconnect
  • 字体域名:若首屏文字马上用到,才 preconnect
  • 其余第三方:延后,或只 dns-prefetch

如果客服系统、广告网络、行为分析、社媒组件在前 1 秒 就冒出 4 到 6 个新域名,浏览器要先分配 DNS、连接、TLS 资源,LCP 图片会排到后面。很多页面不是下载慢,而是请求发起慢。瀑布图里最常见的信号是:HTML 在 200ms 左右返回,LCP 图片大小只有 180KB,但真正开始下载却已经到了 600ms 之后。前半段空掉的时间,往往就在 DNS、TCP、TLS 和资源发现上。

下面几类页面,更容易出现解析链偏长的问题:

  • 使用多个 SaaS 工具拼装首页,首屏第三方域名超过 5 个
  • 图片、CSS、JS、字体分别放在独立子域名
  • 使用外部字体服务,且字体文件有 2 套以上字重
  • CDN 接入层复杂,资源域名带 3 层以上 CNAME
  • 用户分布跨 3 个以上洲际区域,但 DNS 供应商覆盖一般

遇到这种页面,不妨先做一轮收敛,把首页前 14KB HTML 里能发现的首屏资源尽量压缩到更少的主机名。只要浏览器在解析 HTML 后,能更早发现 LCP 图片地址,并且该地址不需要再走很长的 DNS 链路,请求起点就会更早。很多情况下,把首屏域名从 4 个降到 2 个,比把图片从 220KB 压到 160KB 更能改善首屏观感。

排查时可以按下面一组点去看,速度会更快一些:

  • 首屏前 500ms 出现了几个新主机名
  • LCP 图片是否来自首次出现的外部域名
  • 资源域名是否带 2 层以上 CNAME
  • DNS Lookup 在瀑布图里占了多少,是否超过 50ms
  • 首屏是否存在字体、统计、客服同时抢前期连接
  • 是否给真正的首屏域名加了 preconnect
  • 是否把不参与首屏显示的第三方脚本延后到 DOMContentLoaded 或更后

也可以用一个更容易执行的标准来做初筛。如果首页首屏只有 1 张大图、1 份 CSS、少量文本,但 LCP 仍超过 2.5 秒,同时 TTFB 并不高,那优先检查 DNS。如果首页请求很多,但首屏新域名只有 1 到 2 个,DNS 一般不是最先处理的对象。如果首屏资源不大,下载很快,偏偏请求晚,那大多是前面的解析链和连接顺序有问题。

页面改完后,再看两个位置最容易验证结果。一个是首屏瀑布图,确认 LCP 图片请求是否从原来的 500–700ms 提前到 250–400ms。另一个是真实用户数据,观察不同地区访问的 P75 LCP 是否收回 100–300ms

如果实验室数据改善,真实用户数据没明显变化,就继续看地区差异、公共 DNS 响应差异、移动网络首查时间。因为桌面宽带与移动网络下,DNS 解析表现经常会差出 1 倍以上

为了让页面更稳定,首屏阶段更适合“少域名、短链路、低干扰”的结构。浏览器在第一次进入页面时,时间花在哪一段,最终就会体现在 LCP 上。DNS 解析链如果压短了,LCP 图片通常会更早进入下载阶段,首屏最大元素出现得也会更早。

滚动至顶部