浏览器主线程为何如此昂贵

The Browser's Main Thread Is Expensive

浏览器主线程为何如此昂贵

提到前端优化,大家常想到减少网络请求或压缩包体积,却很少关注主线程。在交互密集的场景下,无论网络多快,一旦主线程被阻塞,屏幕就会卡顿。文章指出,代码本身可能并不慢,问题在于它占用了主线程。浏览器的主线程负责 JavaScript 执行、渲染和事件处理,所有工作都在单线程队列中串行执行。如果任务超过 16.6 毫秒的帧预算,就会导致掉帧。解决之道在于智能分配时间:通过拆分长任务、批处理、优先级排序和延迟执行,让主线程在任务间隙处理用户输入和渲染,从而恢复流畅体验。

代码本身并不慢,问题只是它恰好是占用主线程的那段代码。
  1. martinald

    文章写得很好,真希望更多人知道这些内容。

    不过问题在于,这篇文章过于聚焦于交互性。实际上,90% 以上运行缓慢的网站,慢的原因并非真的在于交互性,而是因为它们打包了巨大的 React/Next.js 包,并且需要执行极其繁重的 hydration(水合)工作。

    有_太多_网站的包体积超过 10MB,需要下载、解析并进行 hydration。

    我甚至见过(很多)网站内部嵌套了多个 SPA(单页应用)。

    如果你的网络较慢和/或 CPU 性能较弱,页面基本上会几十秒内完全无法使用,此时无论如何在 bundle hydration 后让出主线程(yielding)都无法真正解决这个问题。

  2. jonathanlydall

    文章太棒了。

    对我来说几乎没有新信息,因为我基于对渲染“线程”如何工作以及如何阻塞的直观理解,已经自己应用过其中一些技术。但对于经验较少的开发者来说,这篇文章应该极具启发性,并能提供对正在发生之事的坚实理解。

    我在大约 15 年前制作的一个业余项目 [0] 中就使用了 yield 技术,特别是当它需要在 canvas 上执行大量绘图操作时。我也曾尝试使用 worker 线程在后台线程上渲染部分组件,但当时无法在它们与主线程之间高效地复制数据,只能将数据作为 base64 编码的 PNG 发送,而编码、解码以及将其复制到 canvas 上的开销,导致其性能远不如直接在主线程上完成所有操作。

    该网站还在 JavaScript 中对上传的文件进行 Gzip 解码,当时我找到的库是同步执行所有工作的,因此很容易让 UI 线程锁定 10 秒以上。我调整了它,使其能够每 200 毫秒左右让出一次控制权,这个过程非常有教育意义,尤其是它让我下定决心永远不要在 if 语句后省略大括号。我花了很长时间试图理解为什么它不起作用,直到最后我意识到我添加的一条语句不在 if 语句的块内。这并不是我不懂 if 语句的工作原理,而是我脑子里觉得缺少大括号也没关系……

  3. nerdralph

    我同意文章中的大部分观点,但想澄清以下几点:

    > 为了让屏幕看起来流畅,帧必须以显示器的刷新率绘制。在最常见的 60Hz 显示器上,这意味着每秒 60 帧,或每帧约 16.6 毫秒。

    完全匹配显示器刷新率并非绝对必要。对于 144Hz 的显示器,72FPS 对绝大多数人来说看起来已经足够流畅,甚至 48FPS 对大多数人来说也很流畅。我同意更高的 FPS 更好,但收益是递减的。

  4. piker

    写得真好,楼主。我们从事 WASM 开发,正在考虑使用 web workers 来辅助在该上下文中将文档渲染任务移出主线程。读到 ArrayBuffer 仅通过引用传递(moved by reference only)时,我感到很惊讶。这可能是一个可行的选项。感谢整理这篇文章。

  5. larodi

    顶级文章,致敬!之前应用过其中一些技术,它们确实非常重要。问题在于,当你学习或教授 JS 时,通常剩下的时间很少来讨论这些话题,而这些话题的重要性往往比表面看起来要大得多。

    协作式多任务处理的核心要点就是时不时地让出控制权(交还给调度器),以便它能处理绘图。此外,在 60fps 下,每 N 帧计算一次的内容非常多,而人眼察觉不到,动画依然流畅。

    当涉及 WebGPU 时,情况会变得更加有趣,不过 CSS 动画器确实非常快。

    注:我最近的一些作品(newskool 数字传单网站)-> bsf.hmsu.org // nouveauxhivers.dub4powder.xyz

  6. gwbas1c

    顺便一提:这不仅仅是浏览器的问题。所有平台(Windows、Mac、iOS、Android)通常都采用单线程 UI。

  7. jkhdigital

    这篇文章以如下陈述作为结尾:

    > 开发中的很多事情都是权衡取舍,你必须根据具体情况做出选择,这最终取决于开发者的经验和判断。

    这是一篇很棒的文章,但我认为它在将调度问题框架化为“经验和判断”的问题上有些滞后。将工作分配给稀缺资源的问题,是计算机科学中最古老、研究最透彻的问题之一。在试图重新发明轮子之前,去查阅一下相关教科书才是合乎逻辑的做法。

  8. skobes

    关于 'FLIP(First, Last, Invert, Play)' 的讨论,如果提到 View Transitions API 会更好,这个 API 基本上就是为了自动化这项技术而存在的。

同日更多故事

2026-09-03