前端缺失的指标:TBT 窗口
摘要
本文介绍了 TBT 窗口这一缺失的前端性能指标概念,它突出显示了从首次内容绘制到可交互时间之间的总阻塞时间,并通过一个案例研究说明,某客户端的 TBT 从 495 毫秒飙升至 5,789 毫秒。
<p><a href="https://lobste.rs/s/8eaumi/front_end_s_missing_metric_tbt_window">评论</a></p>
查看缓存全文
缓存时间: 2026/05/31 16:21
# 前端缺失的指标:TBT 窗口
来源:https://csswizardry.com/2026/06/front-ends-missing-metric-the-tbt-window/
2026年6月1日,发布于 **CSS Wizardry**。
## 目录
独立写作由我出色的 **支持者** (https://csswizardry.com/supporters/) 提供支持。
1. 快速回顾 (https://csswizardry.com/2026/06/front-ends-missing-metric-the-tbt-window/#a-quick-refresher)
2. TBT 窗口 (https://csswizardry.com/2026/06/front-ends-missing-metric-the-tbt-window/#the-tbt-window)
3. 那个让 TBT 崩盘的 LCP 优化 (https://csswizardry.com/2026/06/front-ends-missing-metric-the-tbt-window/#the-lcp-improvement-that-tanked-tbt)
4. TTI 为什么会移动 (https://csswizardry.com/2026/06/front-ends-missing-metric-the-tbt-window/#why-tti-can-move)
5. 因果关系可以是双向的 (https://csswizardry.com/2026/06/front-ends-missing-metric-the-tbt-window/#causality-can-run-both-ways)
6. 我们是如何修复的 (https://csswizardry.com/2026/06/front-ends-missing-metric-the-tbt-window/#how-we-fixed-it)
7. 让窗口可见 (https://csswizardry.com/2026/06/front-ends-missing-metric-the-tbt-window/#make-the-window-visible)
8. 最后的思考 (https://csswizardry.com/2026/06/front-ends-missing-metric-the-tbt-window/#closing-thoughts)
9. 引用 (https://csswizardry.com/2026/06/front-ends-missing-metric-the-tbt-window/#citation)
一个客户项目上的事故(而且这确实是个事故!)让我开始思考我们使用的指标,以及充分理解它们确切定义的重要性。一些定义奇特且经常被忽视的规范让整个团队陷入恐慌,但最终却揭示了我们前端工具链中一个实实在在的缺口。对于我的客户来说,从一次 SpeedCurve (https://www.speedcurve.com/) 合成测试到下一次,他们的总阻塞时间(TBT)从 **495 毫秒** 飙升至 **5,789 毫秒**。这是超过十倍的增幅!很自然,大家全力投入查找原因,我也被叫去 (https://csswizardry.com/contact/) 帮忙。我发现的东西既令人豁然开朗,又让人平静下来,但它确实揭示了我们谈论现代 Web 性能指标时的一个空白。这就是我称之为 “TBT 窗口” 的新型 Web 性能指标 (https://csswizardry.com/2024/11/designing-and-evolving-a-new-performance-score/) 背后的故事。
**需要帮助?** 我帮助公司发现并修复网站速度问题。提供 **性能审计**、**培训**、**咨询** 等服务。
## 快速回顾
总阻塞时间 (https://web.dev/articles/tbt/) 是一个实验室指标,旨在量化页面加载期间主线程的不可用程度。它关注长任务 (https://w3c.github.io/longtasks/)——那些占用 UI 线程超过 50 毫秒的任务——并且只计算每个任务中阻塞的部分。一个 70 毫秒的任务贡献 20 毫秒给 TBT;一个 250 毫秒的任务贡献 200 毫秒。任何任务的前 50 毫秒被有效容忍,超过的部分才被视为阻塞时间。它是一个基于实验室的代理指标,用于衡量更真实的以用户为中心的现场指标——交互到下一次绘制 (https://csswizardry.com/inp-simulator/) (INP)。这部分可能大家都熟悉,但经常被忽视的是:TBT 并非 trace 中所有阻塞工作的无界总和,它是 **首次内容绘制 (FCP) 与可交互时间 (TTI) 之间** (https://developer.chrome.com/docs/lighthouse/performance/lighthouse-total-blocking-time/) 所有阻塞时间的总和。这是理解 TBT 指标本身上下文的关键。
首次内容绘制 (https://web.dev/articles/fcp/) (FCP) 打开窗口,是页面加载时间线上用户看到屏幕上出现有内容物(页面正在响应)的第一个时间点。可交互时间 (https://web.dev/articles/tti/) (TTI) 关闭窗口,是页面被认为可靠可交互的时间点。TBT 就是这两个时刻之间发生的阻塞工作。
这有点笨拙甚至出乎意料,因为 TTI 本身已基本从我们日常词汇中消失了。Lighthouse 10 在从 Lighthouse 8 开始的弃用过程后,从性能评分中移除了 TTI (https://developer.chrome.com/blog/lighthouse-10-0/),原因很合理:TTI 高度可变,对异常网络请求和长任务过度敏感。它的定义也看似随意,难以推理:
> 1. 从首次内容绘制 (FCP) 开始。
> 2. 向前搜索至少五秒的安静窗口,其中安静窗口定义为:没有长任务且飞行中的网络 GET 请求不超过两个。
> 3. 向后搜索安静窗口之前的最后一个长任务,如果没有找到长任务,则停在 FCP。
> 4. TTI 是安静窗口之前最后一个长任务的结束时间(如果未找到长任务,则等于 FCP 值)。
哇。
[图:TTI 计算示意图:从导航开始和 FCP 开始,浏览器向前搜索一个五秒的安静窗口,然后回溯到最后一个长任务以确定 TTI。]
TTI 的确定方式是先找到安静窗口,然后回溯到它之前的最后一个长任务。
然而,TTI 已从报告中退役这一事实,并不意味着它不再影响我们仍然关心的数字。如果你在监控 TBT,TTI 仍然定义了测量窗口的远端边界,TTI 分数的变化可以在你可能意想不到的情况下影响你的 TBT 分数。希望你现在能看到问题了。根据谷歌自己的文档,TTI 对异常网络请求和长任务过度敏感,但如果 TTI 也定义了 TBT 的上限,那么同样的异常可以增加(或减少)你被测量的表面积。**劣化的 TTI 分数会劣化你的 TBT 分数。** 实际上,你仍然在被它衡量。这正是我的客户所遇到的情况。
## TBT 窗口
一旦你把 TBT 视为一个窗口化指标,一些原本令人惊讶的行为就变得容易解释了。TBT 窗口是 FCP 和 TTI 之间的区间,在此期间 TBT 允许计数阻塞工作。如果 FCP 提前了,TBT 窗口就更早打开,所以过去位于 FCP 之前的长任务可能突然落入窗口内。如果 FCP 推迟了,相反的情况可能发生:长任务可能脱离范围,尽管它们并未从页面中移除。另一端也是如此。如果 TTI 提前了,窗口关闭得更早,晚期的长任务可能停止对 TBT 的贡献。如果 TTI 推迟了,窗口保持打开的时间更长,页面加载尾部的更多工作可能突然变得可计数。关键点在于:在所有这些情况中,长任务本身可能没有任何有意义的改变。改变的是 TBT 被允许检查的时间线长度。这个会移动的区间就是我所说的 “TBT 窗口”。
[SpeedCurve 仪表盘显示 Pitchup 的 JS 总阻塞时间从 7 月 25 日的 495 毫秒上升到 7 月 26 日的 5,789 毫秒,同时可交互时间升至 19.98 秒。]
线索是两条线一起移动:TBT 随着其测量窗口终点移动而上升。
查看完整大小/质量 (416KB) (https://csswizardry.com/wp-content/uploads/2026/06/speedcurve-full.png)
在我客户的情况下,他们的第一反应完全合理:TBT 回归看起来像是 JavaScript 回归,而且 SpeedCurve 图表中甚至带有 “JS” 字样,所以团队首先检查了他们的 bundle。他们查找了新的包、第三方变更、标签管理器更新,以及可能悄悄进入发布的任何新的 hydration 或框架成本。这是一个合理的调查方向,但一无所获,也与事实不符。在他们查看的时间范围内,没有任何有意义的与 JavaScript 相关的变更,而且瀑布图讲述了一个更有趣的故事。有很多晚期的长任务,但在 “之前” 和 “之后” 的 trace 之间,它们并没有实质性变化。真正改变的是 TTI。在下面的瀑布图中,我用黄色标注了五秒的安静窗口,并回溯到 TTI,标注为粉色的垂直线。请注意,大多数情况下,两个瀑布图具有相同的特征。
[并排的 WebPageTest 瀑布图,比较之前和之后的运行,黄色安静窗口和粉色 TTI 标记显示之后的运行中 TTI 比之前晚得多。]
变更后,黄色安静窗口晚了很多,将 TTI 推到了几乎整个瀑布图的范围。
接下来,让我们专门看看长任务和阻塞任务。同样,主线程阻塞活动的特征基本保持不变,但被测量的表面积却大得多:
[放大的 WebPageTest 主线程比较,显示两个 trace 中大致相似的阻塞活动,但回归后的 TBT 窗口被测量的部分长得多。]
工作并没有突然出现;指标只是有了更多可计数的时间线。回归之前,页面在大量晚期活动开始之前就被认为是可交互的。回归之后,TTI 移到了大约 20 秒,这意味着 TBT 窗口跨越瀑布图更远,将之前不在范围内的工作拉入了指标。就纯数字而言,团队确实有一个 TBT 回归,但他们并没有他们所认为的 JavaScript 回归。这是一个非常重要的区别,因为这两种解释会导致完全不同的调查方向。一种让你进入 bundle、依赖项、hydration 和第三方;另一种让你进入指标的定义以及移动其边界的条件。如果没有 TBT 窗口的概念,你可能会试图优化根本没有引起变化的代码。我从这件事中学到的实际教训很简单:如果 TBT 在没有明显 JavaScript 原因的情况下飙升,请检查网络。
## 那个让 TBT 崩盘的 LCP 优化
这个故事的有趣之处在于,最初是什么移动了 TTI。我们之前一直在处理 LCP,并且在之前的一天为 LCP 图片添加了一个 `preload`。这正是我喜欢看到的事情:识别出重要的图片,让它更早被发现,帮助浏览器更早获取它,改善用户可见的体验。它确实有效:LCP 优化 (https://csswizardry.com/2022/03/optimising-largest-contentful-paint/) 做了它应该做的事情。但不幸的是,它也改变了网络形态。TTI 的计算不仅仅关心长任务;它还寻找至少五秒的安静窗口,其中安静意味着没有长任务,且 **飞行中的网络 `GET` 请求不超过两个**。仅仅通过 `preload` 引入一个额外的请求,页面突然就违反了网络安静窗口条件。TTI 推迟了,TBT 窗口变得更大,突然之间,之前位于测量窗口之外的长任务被包括了进来。
这就是让性能工作如此有趣,老实说,有时也相当烦人的事情。对一个指标来说完全合理的优化,却让另一个完全不同的指标看起来灾难性地糟糕。更具体地说,一个旨在改善 LCP 的优化导致 TTI 推迟,进而导致 TBT 包含了更多的现有长任务尾部。从用户体验的角度来看,这两个指标关注的是不同的事情,却因为规范中的细则变得高度相互依赖。这不一定是对 Lighthouse、SpeedCurve、WebPageTest 或指标本身的批评(尽管我确实希望 TTI 能被完全淘汰,即使作为窗口化指标)。这是一个提醒:我们的工具正在按照说明书工作,但我们需要阅读说明书。
## TTI 为什么会移动
TTI 奇怪的地方在于,它听起来比实际上更直观。这个名字让人感觉它是对页面何时变得可用的直接度量,但实际计算远比这繁琐和晦涩。如上所述,TTI 同时依赖于长任务和网络活动。后者是我一直不喜欢的 TTI 部分。意图可能是合理的:仍在获取资源的页面可能不如已经安静下来的页面稳定,但阈值本身——五秒内飞行中的 `GET` 请求不超过两个——不可避免地是任意的,并允许网络行为影响一个被许多人解读为可交互性的指标。令人沮丧的是,在 tag 管理器、跟踪、广告、懒加载等充斥的世界里,开发者很少能对网络活动进行精细控制。这意味着 TTI 可能因为长任务而移动,但也可能因为页面未能达到所需的网络安静状态而移动。这里正是如此——一个网络优化改变了网络概况,TTI 移动了,TBT 窗口变大了。
另外考虑渲染阻塞 (https://csswizardry.com/2024/08/blocking-render-why-whould-you-do-that/) `<script>` 的情况。其中包含的任何长任务,根据定义,都免于被纳入 TBT,因为它们位于 FCP 之前。使脚本异步 (https://csswizardry.com/2022/10/speeding-up-async-snippets/#the-new-syntax) 这个明智且值得的优化将切实改善里程碑时间,但现在会使任何这些长任务成为 TBT 候选。换句话说,加快绘制时间可能会负面影响可交互性数字,尽管主线程活动量没有实质性变化。长任务并没有变得更糟,它们只是对指标变得可见了。
## 因果关系可以是双向的
有一件事需要明确:TBT 窗口的扩大并不自动意味着 TBT 回归是虚惊一场。因为 TTI 是根据网络安静度和长任务安静度共同计算的,一个真正的阻塞工作增加本身就可以将 TTI 推后。在 12 秒处添加一个大任务,或者将一组任务沿 trace 向下移动,浏览器可能就不再在之前的位置找到它的安静窗口了。TTI 移出,TBT 窗口扩大,TBT 因为两个原因同时上升:确实有更多的阻塞工作,而且指标现在有更多的时间线来计数它。这是一个重要的区别。TBT 窗口不是用来解释 TBT 的方式,而是一种避免猜测的方式。如果窗口因为 FCP 或网络安静度改变而扩大,你可能在看一个窗口化假象;如果窗口因为真正的长任务活动变糟而扩大,那就是一个具有更大表面积的真实 TBT 回归。无论如何,绘制出窗口可以提供所需的上下文来区分这些情况。
## 我们是如何修复的
我们没有修复。这在任何真实的、面向用户的意义上都不是回归,而且我们几乎无法控制根本原因。撤销 `preload` 更改会改善数字,但会实质性地降低用户体验。相反,我建议客户设置一个辅助杠杆:只有当 TBT 独立于 TTI 移动(或以比 TTI 更快的速度移动)时,才将其视为回归。现在所有 TBT 图表都包含一个匹配的 TTI 分数,只有偏差超过某个阈值时我们才会采取行动。如果你在监控 TBT,我建议你也这样做。
**需要帮助?** 我帮助公司发现并修复网站速度问题。提供 **性能审计**、**培训**、**咨询** 等服务。
## 让窗口可见
我认为这是合成工具可以极大地帮助开发者的地方。任何绘制 TBT 的地方都应该同时绘制 TBT 窗口。不是作为一个新的分数,而是作为一种健全性检查或对照。数据已经存在。如果工具知道 FCP 和 TTI,并用它们计算了 TBT,它也可以显示它们之间窗口的大小。在图表上那额外的一条线将让许多调查更容易推理。工具甚至可以关联...
相似文章
eTPS 网站计划 – 简单排行榜 + 您将实际看到的内容
作者介绍了effectiveTPS的网站计划,这是一款使用新的'eTPS'指标(有效TPS)以及原始速度和延迟来比较本地AI模型的工具。其目标是提供一个简单的排行榜,突出展示有用的输出质量,而非原始营销数字。
Agent Execution Tax:浏览器代理基准测试的新衡量指标
Fireworks AI 和 Notte 在运行了四个 LLM 的 720 个浏览器代理任务后,引入了 'Agent Execution Tax' 指标,发现执行可靠性——而非智能——是智能体 AI 的主要瓶颈,其中一个模型在格式错误的 JSON 上浪费了 22.9% 的推理调用。
使用图论加速后端(2019年)
Sensor Tower 工程团队利用图论分析和性能分析工具,识别出后端端点缓慢的瓶颈,通过优化 Protobuf 解码和编码步骤,实现了四倍的速度提升。
为变更优化,而非应用性能
本文指出,软件团队常常过度优化微性能基准测试,却牺牲了开发者体验和工程吞吐量,而这两者才是长期交付速度与可维护性的真正瓶颈。
我们应该摒弃平均CPU利用率
本文解释为何平均CPU利用率对于延迟敏感型工作负载是一个误导性指标,利用排队论和一个真实的生产事故案例,主张采用更细致的监控方法。