原作者: Medhat Dawoud
原文链接:https://medhatdawoud.net/blog/what-makes-the-web-slow
在上一篇文章中我解决了性能优化背后的动机。在这篇文章中,我将简要解释 web 是如何工作的,并解释 web 缓慢背后的核心原因,特别是浏览器的主线程。
Web是如何工作的
首先,我们需要了解从在浏览器中编写域名开始,直到拥有一个完全交互的网页,Web是如何工作的。
网站通常托管在具有 IP 地址 (x:x:x:x) 的服务器上,该 IP 地址附加到名为域的名称,例如example.com,因此当用户在浏览器中的地址栏,然后按回车键,这是短暂发生的事情:
-
浏览器尝试联系 DNS 服务器,搜索附加到您输入的名称的 IP 地址。
-
在浏览器收到正确的 IP 地址后,它会发送从服务器加载页面的请求(在进行 TLS 握手之后)
-
然后浏览器从服务器接收字节流并根据其 MIME 类型对其进行翻译。
-
然后浏览器开始根据其类型处理每个文件以呈现 HTML 并编译 JS 文件并绘制 CSS 文件等等。
-
在网站完全加载之前,浏览器正忙于为您完成所有这些工作以使网站完全交互,然后浏览器可以空闲等待第一个用户输入。
上述所有这些过程通常需要几秒钟,因此您大多数时候都不会真正注意到它。但可能需要更多时间,具体取决于您对过程中各个方面的实施和理解,当然还有客户端的硬件。
从上面的场景中,我们有 3 个主要角色可能在互联网的缓慢中发挥作用:
-
我们托管文件的服务器,
-
我们的在线客户用于使用我们网站的客户端(浏览器)
-
中间当然是网络
网站速度慢的核心原因
-
服务器速度慢:它导致你的服务器响应延迟或高延迟,这当然会导致网站速度变慢。
-
慢速连接:如果网站在第一次加载时就加载了很多文件,这将是一个非常缓慢的FCP和TTFB,例如在3G慢速连接中。
-
大型图片/视频:肯定有大型图片或视频的加载时间过长,可能会阻断主干道的时间。
-
太多的第三方:大多数网站都在使用大量的第三方,如分析和同意管理器等等,它们可能会阻碍你的主要内容的加载。
-
庞大的包库:几乎所有的网站都在漫不经心地使用一些npm包,而不太注意它的包库大小或速度性能,或者对tree shaking 不关心。
-
不好的实现:使用不好的算法来处理数据的规模可能会造成很大的影响
-
较大的加载DOM大小:这将导致每次改变父元素时都会阻塞到主线程,这也会导致渲染的一些缓慢和用户输入响应性的延迟。
-
庞大的文件包:将所有的网站代码放在一个或几个文件中会使它们变得太大,明智地分割它们,只加载用户需要的东西是一个挑战。
上述所有原因都是由于浏览器的工作方式以及客户端和服务器的硬件造成的,在这篇文章中,我更多的是解释为什么 Web 浏览器很慢,并且需要一些理解来使浏览器快速,或者至少对Web用户能觉得更快。现在让我解释一下。
浏览器是单线程的
自从 Web 浏览器诞生以来,它们只有一个主线程,这使得浏览器“理论上”一次只能做一件事,要么渲染 HTML,要么基于 CSS 绘制 UI,要么解码和绘制图像,要么编译和运行 JavaScript。 其中的每一项任务都需要从主线程花费一些时间,这是慢速网站的主要问题,有一些长任务会长时间阻塞主线程,而不会让其他等待任务甚至部分执行。
为了减轻可能有点棘手的主线程上的任务负载,一种解决方案可能是将任务拆分为较小的可执行任务,这将使主线程能够在所有任务之间切换,并使网站感觉流畅和功能齐全 时间
您可能想知道,如果它只有一个,为什么我们称它为主线程? 这是一个好问题,因为您基本上可以使用 Web Worker 将一些繁重的工作从它转移到其他一些并行线程(您创建),并且可以使用一些浏览器 API 或使用一些使生活更轻松的库将它们连接在一起 工人,例如 chrome 团队的 comlink,我们可以在另一篇文章中详细说明。
文件类型影响渲染时间吗?
那当然!相同大小的不同文件可以在相同的网络条件下同时下载,但这里的重点是下载后是什么,浏览器如何处理呢? 所以在下面的例子中,我们有 2 个文件,一个是 170kb 的 JavaScript,另一个是相同大小的图像。
正如你所看到的图像,下载之后,然后解码并最终绘制,基于慢速 3G 和 Moto G4 移动(中间层)的测试,下载时间大约需要 3.4 秒,解码约 64 毫秒,最后约 28 毫秒 被光栅化绘制。
另一方面,JavaScript 文件需要同样多的时间来下载 3.4 秒,然后它进入解析/编译过程,需要大约 2 秒,最后是执行,需要大约 1.5 秒,这两个文件之间有多么大的差异 。所以我们发现 JavaScript 是您网站中最重的资产,它不仅与文件大小有关。
长任务的问题
不幸的是,一个主线程在一个任务中启动它不能被中断,如果我们阻塞它太久用户会觉得网站有点滞后,大多数屏幕都以 60fps 运行,这意味着浏览器应该每帧绘制一个新的帧~ 16 毫秒。
这就是为什么浏览器将任何超过 50 毫秒的任务视为一个长任务,它会下降大约 3 帧或更多帧,用户可能会注意到渲染速度很慢,这通常会带来糟糕的用户体验和更多等待用户输入响应的时间。
我们可以做些什么来增强主线程?
由于主线程是(网络)浏览器的瓶颈,我们可以减轻它的负担吗? 或者至少使它具有某种性能,一个快速的答案是肯定的,我们可以通过以下方式使主线程具有性能:
-
制作一般较小的任务(图像、JS 文件、CSS 文件、字体等)
-
将繁重的任务转移到 Web/Service worker 上
-
也许在 SSR(服务器端渲染)上运行您的应用程序,它将在服务器上进行渲染并卸载主线程
-
避免多余的重绘,因为任何时候它发生都会导致浏览器回流,这是昂贵的
-
考虑使用慢速设备构建您的应用程序,这也将使其在所有其他设备上更快
并且还可以应用更多的优化,一般尽量让主线程尽快空闲。
结束语
在本文中,我们讨论了 Web 的工作原理以及网站运行缓慢的原因,重点介绍了浏览器单线程的主要原因,讨论了文件类型的影响以及 JS 文件与图像相比如何花费更长的时间来处理 相同的文件大小,最后一些快速的想法来减轻主线程的压力。