本文为翻译
原文标题:How we cut 99% of our JavaScript with Qwik + Partytown
原文作者:Miško Hevery
原文地址:builder.io/blog/how-we-

Builder.io 兴奋地向大家的宣布,采用 Qwik 之后,我们的主页即使在移动端,其 PageSpeed Insights 跑分也能达到 100/100。

无论你的应用程序有多大,Qwik 都能帮助网页达到这样的性能。我们用 一系列炫酷的技术 得到了这样的分数,这些技术包括:

  • 页面由 Qwik 提供服务,启动(boot)所需的 JavaScript 不到 1kb
  • 我们的主页只会发送 折页上方(Above the fold)的内容。
  • 使用了 Partytown,将 所有第三方脚本 转移到 web worker
  • 这个站点是通过 builder.io 的 无代码编辑器 创建的

译者注:Above the fold(折页上方),最初用于出版业,指的是报纸头版的上半部分,这个位置一般会用于刊登重要的新闻或照片。而报纸一般会以折起来的形态贩卖给顾客,所以编辑一般会把最重要的新闻放在 折页上方 。
到了互联网时代,有时会使用 折页上方 表示网站不需要滚动或点击就能看到的部分。而相对的 Below the fold 表示 需要用户交互才可见的部分。

详见 wordstream.com/below-th

Qwik 可以扩展到 包含数百个组件、拥有MB级内容 的 大型站点,并依旧能保持页面的快速载入。它还提供了 可转换为客户端组件 的 交互式的服务端组件。

无头(Headless)站点 拥有难以置信的 载入速度 和 灵活性。它使得我们可以轻松和任何一家 API-first 服务供应商 对接整合,但这也会带来持续的成本,因为不管任务有多小(比如,向页面添加一个按钮),都依赖持续的开发资源。Builder.io 通过 可视化编辑器 解决了这个问题,该编辑器让 非开发人员 能使用 开发者提供(且经过授权)的元素 构建 高性能页面。

优化前

事情一开始是这样:

注意,此时站点的性能中规中矩。在移动端,Google PageSpeed 推测 用户需要 7.6s 才能点击一个链接,得到响应。这样的用户体验很糟糕。多提一嘴,Google 会利用 PageSpeed 跑分 调整 SEO 的排名。

之所以性能这么差,是因为站点启动时,必须执行一大堆的 JavaScript。现如今,就算是静态站点也会塞满 JavaScript,用来添加菜单,交互性逻辑 和 第三方统计脚本,例如:Google Tag Manager,Intercom 和 CRM 服务。

JavaScript 网页降速 的原因有很多,但究其根源,可以分成两类,来此站点自身 和 来此第三方脚本。

第一类网页降速的原因来自 框架。现代框架 提升了 网页的开发体验 和 交互性。但这是以 大量的JS下载 和 缓慢的启动时间 作为代价的。因为,框架需要 reconcile 服务端生成的 HTML 和 框架所期望的 DOM 结构。这个过程就是所谓的 Reconciliation / Rehydration(协调/注水),所有的框架(除了 Qwik)都避免不了它。Reconciliation / Rehydration 的 关键环节是当监听器添加到 DOM 后,让页面具备可交互性。这就是为什么 必须尽快进行 Reconciliation / Rehydration。如果它没有完成,你的网站就没法正常进行工作(比如 菜单,聊天组件什么的)。

第二类网页变慢的原因来自 第三方脚本。没错,很多 demo 站点 和 “新建站点” 在 PageSpeed 跑分中表现都还不错,但这很大程度上是由于 第三方脚本 还没加进去。下面是一些在我们站点中引入的第三方脚本:

  • Google Tag Manager:对每一个活跃的站点来说,都是必要的,它可以收集到有用的统计数据,市场营销人员 可以通过这些数据洞察到网站的使用情况,并做出改进。GTM 会在一开始执行,在 PageSpeed 开始扣分之前,它就会抢占所有 PageSpeed 分配给网站的 CPU 时间。
  • Intercom:让 客户 可以和 网站创建者 在页面上实时聊天,提出疑问 或 获取更多信息。
  • Twitter:我们加载 Twitter 提供的 JavaScript 后,就可以在 Twitter 的小组件里展示我们产品的推荐信息。

所有上述的第三方脚本都会在加载页面时就地立即执行,并与上文提到的 Reconciliation / Rehydration 阶段抢占 CPU,这会导致糟糕的用户体验。

问题在于,作为开发者,我们几乎无法控制上述情况。我们必须使用第三方脚本来增添一些 市场营销团队需要的 分析功能 和 用户服务功能,而且我们使用的现有框架都有着高昂的 Reconciliation 现场启动成本。供我们使用的控制手段并不多。这是我们行业的现状,这就是为什么常规方案的优化效果都非常一般。

Qwik 和 Partytown 就是为解决这类问题而生的!

优化后

指标 之前 之后 单位 %
性能得分 52 100 s 92%
First Contentfull Paint 3.4 1.1 s 309%
Speed Index 3.4 1.1 s 309%
Largest Contentful Paint 3.8 1.2 s 316%
Time to Interactive 7.6 1.4 s 543%
TTI - LCP (difference) 3.8 0.3 s 1,266%
Total Blocking Time 1,300 40 ms 3,250%
Cumulative Layout Shift 0 0 -

首先,我要提醒大家,这些数字对应的是移动端,移动端的性能优化 可比桌面端难得多。

上表展示了我们目前使用 Qwik 和 Partytown 优化后的网站性能。提升相当明显。TTI(可交互时间,Time to interactive) 从 7.6s 降至 1.2s。累积阻塞时长(Total Blocking Time)从 1.3s 降至 40ms。JS 执行量的降低 可以直接归功于 Qwik 处理了框架的问题 而 Partytown 处理了 第三方库的问题。

上图是使用 Qwik/Partytown 前的 性能分析报告。(模拟移动端)

  • 页面加载 1.8s。
  • 主线程大部分时间忙于 “Reconciliation” 工作(确定 DOM 监听器的应当放置的位置)。
  • 上面的过程导致了掉帧。
  • 主线程忙于 “Reconciliation” 前,有一波 JS 代码 加载。

我们比较一下应用 Qwik/Partytown 这对组合后 和 之前有什么不同:

  • 没有 JS 下载。
  • 页面加载 0.5s。
  • 主线程基本处于空闲状态。
  • 几乎不掉帧。
  • Partytown 随后加载
  • 第三方脚本 在 web worker 中执行(而不是在主线程中)

这对比简直是一个天上一个地下。

重点其实不在于 Qwik/Partytown 采用了什么聪明的算法。相对的,Qwik/Partytown 几乎卸掉了主线程中所有的 JS,这就是页面载入速度这么快的原因。即使在几乎没有 JS 的情况下,Qwik 仍能保证页面具备完全的交互性。Qwik 会让你尝尽甜头。我们看看 JavaScript 使用情况。

minified compressed
基线 (主线程 JS) 1,800kB 326kB
Qwik + Partytown (主线程 JS) 3.5kB 2.5kB
--> 部分: Qwikloader .5kB 1kB
--> 部分: Partytown confg .5kB 1kB
--> 部分: Partytown 2.5kB 1.5kB
=== 文件大小优化 === 51,429% 13,000%
WebWorker 第三方 JS 219kB 82kB
--> 部分: Zoominfo 1.5kB 1.3kB
--> 部分: Google Tag Manager 167kB 60kB
--> 部分: Google Analytics 50kB 21kB
--> 部分: site-tracking 217kB 64kB

我们把 主线程上 1.8MB 的 JavaScript 降到了 3.5kB。卧槽!

最初,网站需要 1.8MB JavaScript,其中 219kB 是 第三方脚本,作为开发者,我们没法控制这些脚本。剩下 1.6MB 的 JavaScript 是站点自己的。这 1.6MB 包含 框架、模版 和 样式,这些资源需要经过 Rehydrate,才能使站点进入完全可交互状态。如果使用常规的框架,你需要下载两次内容。一次是 HTML,一次是 JavaScript 。两次下载总计 1.6MB 代码。(这是一个网站模板,压缩得很完美了,只有244kB)。

将 基线代码 和 Qwik + Partytown 进行对比,后者只有 3.5kB(压缩后 2.5kB)。我要再次澄清一下:使用 Qwik + Partytown 后,主线程上执行的 JavaScript 只会有 3.5kB!这就是为什么网站可以如此迅速的加载,主线程已经没有别的事要做了。另外我要强调一点,3.5kB 是一个固定的成本,不管你的网站变得多么复杂,它都不会改变。

执行第三方代码的问题依旧存在,但是这些问题已经被转到了 WebWorker 线程,他们执行的优先级是低于 主线程的。全部的 220kB 第三方代码 可以在不影响 主线程性能 的情况下 完成自己的任务。

我还想指出一点。上文提到现有框架需要下载两次站点内容。一次是 HTML,一次是 JavaScript,总共 1.6MB。这里就是 Qwik 的用武之地。Qwik 会将这 1.6MB 打散成多个独立的部分。Qwik 可以在 用户交互 时,只下载全部 JavaScript中的一小部分 。Qwik 可以 乱序延迟进行组件的 rehydrate 操作。这意味着在用户与页面进行交互之前不需要 JavaScript。而由于每个组件的 hydrate 过程是独立的,当用户和某个组件进行交互时,我们只需要下载执行那个组件的一小点 JavaScript 进行 hydrate。这样做有两个优点:

  1. 页面启动时,我们什么都不需要做,而且
  2. 当我们不得不进行 rehydrate 时,其范围只会是单个组件(而不是整个页面)。

最后我想说的是,页面上的绝大部分都是静态的,这意味着这些组件永远不需要 hydrate,因此 这些组件的 JavaScript 永远也不会下载。

Qwik 是什么?

Qwik 是一种新型的 web 框架,它致力于 缩短 TTI。Qwik 具有 可恢复性(Resumability)。这体现在:Qwik 可以在服务端执行,可以被序列化为 HTML,被发送到 客户端。在客户端,qwikloader.js(在客户端上小于 1kb 的 JS)会静静等待用户交互。当用户开始交互,Qwik 可以从服务器停下的地方接着执行。可恢复性 还体现在:Qwik 不需要在页面启动时做 reconciliation,而是将 reconciliation 安排到用户和 需要 hydrate 的组件交互 的时候进行。Qwik 可以在服务端创建组件 并无缝将其带到客户端。这些特性能够为应用程序带来 即时启动能力(Instant-on),就像我上面描述的一样。

懒加载 折页下方(Below the fold)的内容

Qwik 将自己所有的状态保持在 DOM 中,这意味着 Qwik 本身是无状态的。无状态属性让 懒加载折页下方的内容 成为了可能。

动图封面

现有框架很难实现这种操作,但对 Qwik 来说简直是小菜一碟。

Partytown 是什么?

Partytown 可以帮助你将 第三方脚本 迁移到 web worker 中。第三方脚本 往往是导致页面 TTI 过长的 罪魁祸首。

下一步计划

我们正在努力工作,希望能尽快将 Qwik 送到你手里,这样你就能亲自体会到 Qwik 的惊人的能力。

我们是如何利用 Qwik 和 Partytown 削减掉 页面中 99% 的 JavaScript 的
标签: