0

    如何一次浏览器性能提升 10 倍?

    2023.08.05 | admin | 149次围观

    最近,我们把 Universe.com 的主页性能提高了 10 几倍。让我们一起来探索一下我们是如何实现这个结果的,涉及到了哪些技术。

    一开始,我们先来看看,为什么网站性能如此重要(在本文末尾附有本案例研究的链接):

    在本文中,我们将简要介绍帮助我们提高页面性能的以下几个主要方面:

    针对某些情况,我们的主页是用 React(TypeScript)、Phoenix(Elixir)、Puppeteer(无头 Chrome)和GraphQL API(Ruby on Rails )构建的。在移动设备上的界面如下所示:

    Universe homepage 和 explore

    性能测量没有数据js常见浏览器兼容问题,只不过是空谈。—— W. Edwards Deming

    实验室测试工具(Lab instruments)

    实验室测试工具允许在受控环境中,用预定义设备和网络设置采集数据。借助这些工具,调试任何性能问题和具有良好重现性的测试就变得更加简单。

    Lighthouse是在本地计算机上审核 Chrome 页面的出色工具。它还提供一些关于如何提高性能、可访问性、SEO 等有用技巧。下面是一些模拟 Fast 3G 和 4 倍 CPU 减速的 Lighthouse 性能审核报告:

    用 First Contentful Paint (FCP) 提高 10 倍性能的前后对照

    然而,只使用实验室测试工具的缺点是:它们不一定能发现真实世界的瓶颈问题,这些问题可能取决于终端用户的设备、网络、位置和很多其他因素。这就是为什么使用现场测试工具也很重要的原因。

    现场测试工具(Field instrument)

    现场测试工具使我们可以模拟和测量真实的用户页面负载。有很多有助于从实际设备中获取真实性能数据的服务:

    WebPageTest 报告

    渲染

    渲染内容的方法有很多,每种方法都有其优缺点:

    客户端渲染

    之前,我们把我们的主页和 Ember.js 框架一起实现为具有客户端渲染的 SPA。我们遇到的一个问题是,Ember.js 应用程序包太大。这意味着,在浏览器下载、解析、编译和执行 JavaScript 文件时,用户只能看到一个空白的屏幕。

    白屏

    我们决定用React重建该应用程序的某些部分。

    预渲染和服务器端渲染

    例如,用React Router DOM构建的客户端渲染应用程序的问题, 仍然和 Ember.js 的相同。JavaScript 开销大,并且需要一些时间才能看到浏览器中的首次内容绘制(First Contentful Paint)。

    当我们决定使用 React 后,我们马上就用其它潜在的渲染选项进行试验,以让浏览器更快地渲染内容。

    使用 React 的常规渲染选项

    这就是我们为什么决定尝试一些混合方法的原因,尝试从每个渲染选项中获得最佳效果。

    运行时预渲染

    Puppeteer是个 Node.js 库,它允许使用无头 Chrome。我们希望让 Puppeteer 试试在运行时进行预渲染。这支持使用一种有趣的混合方法:服务器端用 Puppeteer 渲染,客户端用激活渲染。这里有一些谷歌提供的有用窍门,关于如何使用无头浏览器来进行服务器端渲染。

    用于运行时预渲染 React 应用程序的 Puppeteer

    使用这种方法有如下优点:

    然而,我们在使用这个方法时遇到了一些挑战:

    使用 Puppeteer 进行服务器端渲染的体系结构

    在 AWS Lambdas 和 GCP 函数上的 Puppeteer 响应时间

    随着我们越来越熟悉 Puppeteer,我们已经迭代了我们的初始方法(如下所示)。我们还进行着一些有趣实验,通过一个无头浏览器来渲染 PDF。还可以使用 Puppeteer 来进行自动端到端测试,甚至都不用写任何代码。现在,除了 Chrome,它还支持 Firefox。

    混合渲染方法

    在运行时使用 Puppeteer 很具挑战性。这是我们为什么决定在构建时使用它,并借助一个在运行时可以从服务器端返回实际用户生成内容的工具。与 Puppeteer 相比js常见浏览器兼容问题,它更稳定,并且吞吐量更大。

    我们决定尝试一下 Elixir 编程语言。Elixir 看起来像 Ruby,但是运行于 BEAM(Erlang VM)之上,旨在构建容错且稳定的系统。

    Elixir 使用Actor 并发模型。每个“Actor”(Elixir process)只占用很少的内存,约为 1-2KB。这样允许同时运行数千个独立进程。Phoenix是一个 Elixir web 框架,支持高吞吐量,并在独立的 Elixir 过程中处理每个 HTTP 请求。

    我们结合了这些方法,充分利用了它们各自的优点,满足了我们的需要:

    Puppeteer 用于预渲染,而 Phoenix 用于服务器端渲染

    我们可以继续构建一个简单的浏览器 React 应用程序,不需要在终端用户设备上等待 JavaScript 就可以快速加载初始页面。

    这让内容 SEO 变得很友好,允许根据需要处理大量不同的页面,并且更容易扩展。

    这样,我们可以构建高度交互的应用程序,和访问 JavaScript 浏览器功能。

    使用 Puppeteer 进行预渲染、使用 Phoenix 进行服务器端渲染和激发使用 React

    网络

    内容分发网络(CDN)

    使用 CDN 可以实现内容缓存,并可以加速其在世界范围内的分发。我们使用Fastly.com,它为超过 10% 的互联网请求提供服务,并为各种公司使用,如 GitHub、Stripe、Airbnb、Twitter 等等。

    Fastly 允许我们通过使用名为VCL的配置语言编写自定义缓存和路由逻辑。下图显示了一个基本请求流的工作原理,根据路由、请求标头等等来自定制每个步骤:

    VCL 请求流

    另一个提高性能的选择是在边缘使用 WebAssembly(WASM)和 Fastly。把它想象成使用无服务器,但是在边缘使用这些编程语言,如 C、Rust、Go、TypeScript 等等。Cloudflare 有个类似的项目支持Workers上的 WASM.

    缓存

    尽可能多地缓存请求对提高性能很重要。CDN 级别上的缓存可以更快地为新用户提供响应。通过发送 Cache-Control 头来缓存可以加快浏览器中重复请求的响应时间。

    大多数构建工具(如Webpack)允许给文件名添加哈希值。可以安全地缓存这些文件,因为更改文件将创建新的输出文件名。

    版权声明

    本文仅代表作者观点。
    本文系作者授权发表,未经许可,不得转载。

    发表评论