
Framer与Google合作修复Lighthouse模拟缺陷,Lighthouse 13.6提升Framer站点评分
来源标题:Technical/eng breakdown of Framer's Lighthouse 13.6 optimizations
Framer与Lighthouse团队合作一年,修复了慢速手机模拟中的两个缺陷,Lighthouse 13.6让Framer站点在PageSpeed Insights中的评分更接近真实用户体验。部分Framer大品牌客户评分提升12至25分,站点无需做任何改动即可获得新评分。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
快速的 Framer 站点在 PageSpeed Insights 里可能显得很慢。我们花了一年时间与 Lighthouse 团队合作,修复其慢速手机模拟中的两个缺陷。Lighthouse 13.6 让这些分数更接近访客的真实体验。
一个 Framer 页面在真实访客面前可以在 300 毫秒内绘制出主要内容,却仍然在 PageSpeed Insights 里得到黄色分数。多年来,客户一直问我们为什么。多年来,我们的回答一直是“因为 PageSpeed Insights 在这里算错了”——而当你面对的是 Google 时,这个回答很难说出口。
所以我们和 Google 一起解决这个问题。上周发布的 Lighthouse 13.6 让 Lighthouse 在 Framer 站点(以及许多其他单页应用)上更准确。Framer 上一些最大的品牌提升了 12 到 25 分:
你不需要在自己的站点上做任何改动就能获得新分数。
Lighthouse(以及运行它的 PageSpeed Insights)实际上并不会在慢速手机上加载你的页面。那样又慢又嘈杂。相反,它先在快速连接上加载页面,记录每一个请求,然后模拟这些请求在慢速手机上会如何发展。这个模拟叫做 Lantern。
棘手的地方在于弄清楚哪些请求真正拖慢了加载。Lantern 走了一条捷径:任何在页面变为可见之前发生的请求,都被假定为拖慢了页面。唯一的过滤条件是优先级:First Contentful Paint(FCP)忽略低优先级请求,而 Largest Contentful Paint(LCP)几乎什么都不忽略。
浏览器在 style.css 加载后立即绘制。script-1.js 和 font.woff2 恰好在那之前加载。
Lantern 在慢速连接上模拟加载。script-1.js 和 font.woff2 并不会延迟绘制,但它们在绘制之前加载且优先级很高,所以 Lantern 认为它们延迟了绘制。
这对 Framer 来说是个问题。为了让站点加载得快,我们在 HTML 中直接告知浏览器页面需要的每一个 JavaScript 模块(<link rel="modulepreload">),并内联页面的 CSS,包括字体引用(@font-face)。浏览器会尽早并行获取所有这些内容,不阻塞任何东西——但带着高优先级。Lantern 看到一堆高优先级请求提前完成——于是断定页面必须等它们全部加载完才能绘制。
结果呢?在真实设备上,站点很快,但在 Lighthouse 里,它的 FCP 和 LCP 很差。如果我们去掉预加载(我们试过!),页面在 Lighthouse 里变快了,但对真实访客变慢了。显然,这并不好。
Framer 预加载页面需要的每一个 JS 模块。这让页面很早就可交互。但 JS 模块在真实 FCP 之前加载,所以 Lantern 让模拟的 FCP 等待它们全部加载。
没有预加载时,浏览器会逐个发现并下载每个 JS 模块。页面可交互的时间晚得多。但 JS 模块现在在真实 FCP 之后加载,所以 Lantern 忽略它们,模拟的 FCP 就提前了。
我们在 2025 年 6 月将此报告为 Lighthouse issue #16539。它现在是 Lighthouse 仓库中第二多点赞的 issue。
经过一年的推动、Chromium 变更以及和 Google 员工的会议交流,Lighthouse 13.6.0 于 10 月 27 日发布,带来了两个修复。
Chrome 已经知道哪些请求会阻塞渲染。它发出的每个请求都带有 renderBlocking 状态:blocking、non_blocking,以及其他几个值。Lantern 却从未查看过它——而是根据请求的优先级和类型来猜测,所以一个高优先级的预加载“看起来”会阻塞渲染。
Lighthouse 13.6 现在向 Chrome 询问这个状态。如果 Chrome 说某个请求不阻塞渲染,Lantern 就不再让 FCP 等待它。在 Framer 站点上,这去掉了很多等待:
这随 Chromium change 8508087 和 Lighthouse PRs #16869 / #17307 发布。
这是 react.dev 的首页。屏幕上最大的东西是一块文本:
文本在 1.2 秒出现,然而 Lighthouse 13.5 报告它的 LCP 在 6.0 秒。
Lighthouse 13.6 识别出这块文本出现在首次绘制中:FCP 和 LCP 都落在 1.2 秒。
这块文本在 HTML 中,所以它出现在浏览器绘制的第一帧里。FCP 和 LCP 在同一时刻发生——字面意义上在同一帧内。
Lantern 不知道这一点。它分别估算 FCP 和 LCP,而 LCP 估算会等待(几乎)每一个在绘制之前完成的请求。所以 Lighthouse 会报告 FCP 在 1.2 秒、LCP 在 6 秒——针对同一块文本!
Lighthouse 13.6 增加了一条简单的规则:如果真实的 FCP 和 LCP 发生在同一次绘制中,且 LCP 元素是文本,那么模拟的 LCP 就等于模拟的 FCP。(#16782,随 #17379 发布。)
有一件事仍未修复。当 LCP 元素是图片时,Lantern 仍然把图片之前完成的几乎每一个请求都算作图片等待过的东西:模块预加载、字体、fetch、脚本。修复 1 只适用于 FCP。
<!doctype html> <body> <img src="hero.png" /> <script type="module" src="small-script.js"></script> <!-- This script makes a couple of fetch requests when loaded --> </body> <!doctype html> <body> <img src="hero.png" /> <script type="module" src="small-script.js"></script> <!-- This script makes a couple of fetch requests when loaded --> </body> <!doctype html> <body> <img src="hero.png" /> <script type="module" src="small-script.js"></script> <!-- This script makes a couple of fetch requests when loaded --> </body>如果脚本及其 fetch 请求恰好比图片先完成,Lantern 就会模拟图片等待它们全部完成。在快速测试连接上,无关请求提前完成非常常见,所以
我们提出了一个我们认为合理的修复方案。当 Lighthouse 模拟 LCP 时,它实际上不是模拟一次,而是两次:一次乐观地(“如果一切顺利/除了最明显的东西之外没有真正阻塞 LCP 的东西会怎样”),一次悲观地(“如果一切都不顺利/一切都在阻塞 LCP 会怎样”)。然后取两者的平均值作为最终的 LCP。
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。