打造世界上最快的网站以及其他错误
这是一个关于很多事情的故事:
- 将财富 20 强网站限制在 20kB 以内
- 深入研究网站速度,我们会看到长着尖牙的鱼
- React 阻碍了我为用户提供真实服务的目标
- 竭尽全力做正确的事情
- 最后,我鼓励你尝试一些代码。
情况:令人沮丧的典型
我负责 Kroger 区域连锁店的电商网站建设,这些网站大部分都共享一个代码库。你大概能猜到前端堆栈:React、Redux,以及它们常见的 JavaScript 泛滥问题。
事实:
-
在任何功能代码之前,使用的 React/Redux 包总计 44.7 kB 。
-
我们的 WebPageTest 结果不言而喻。
-
这是在投资服务器端渲染 (SSR)、性能团队和自动回归测试之后。
具体来说, React SSR 就是那些看起来速度更快但外表可能具有欺骗性的变化之一。回想起来,我很惊讶开发人员竟然认为 SSR + rehydration 是一种改进。
通过运行两次,让您的代码更快!
— React SSR 的工作原理
背景故事:开发人员被放射性 WebPageTest 咬伤
我曾经建议其他开发人员不要再写速度慢的代码。1例如……
-
“请减少
<div>
s,它们会使我们的 DOM 变得又大又慢。” -
“请避免使用类似 CSS
.Component > * + *
,它与我们的大 DOM 结合会导致明显的滞后。” -
“请不要把 React 用于所有事情,它会限制我们的速度。”(特别是如果它渲染具有复杂样式的大型 DOM……)
没人听。不过说实话,他们为什么要听呢?
这种情况持续了下来,而且很酷/很酷/令人沮丧/很酷。但是,新的设计系统对 Tailwind 造成了足够的影响,导致桌面首次绘制时间缩短了 0.5 秒,这足以与专门的 Web 性能团队进行协商。
一切进展顺利,直到事情变得糟糕。瞧,这就是速度优化团队的行业标准生活:
- 通过改进构建配置、删除重复库和删除死代码等无争议的改变取得成功
- 审核其他团队的代码并提出改进建议
- 上述建议之后,我们自己进行改进,但始终未能摆脱积压
- 尝试通过 bundle size 监控、PR 中的 Lighthouse 检查以及其他新的流程层来实现改进
- 听到人们因为必须遵守上述层层程序而发出哀号和咬牙切齿的声音
- 意识到我们需要解释为什么我们惹恼了其他人,否则我们就会被认为对利润产生负面影响
问题是,WebPageTest 对我们的速度皱眉并没有转化为糟糕的移动流量 - 事实上,大多数用户都在使用 iPhone。2从商业角度来看,当图表向上和向右移动时,谁会在乎网站是否可以更快?
为了证明我们没有浪费大家的时间,我们使用了WPO Stats和内部数据来计算,每 kB 的客户端 JavaScript 每年花费我们约 100,000 美元,而直到可交互时间到来之前的每一毫秒至少花费 40,000 美元。3
但证明速度=金钱只会让我们从愤怒进入对性能的悲痛的讨价还价阶段:囤积改进以供日后使用、在截止日期后修复大量回归的空洞承诺,以及以“开发人员体验”为诉求的抗议数字。
拒绝 | 速度够快了。你看过那些 M1 基准测试了吧? |
---|---|
愤怒 | 你的意思是我也得关心这个!?我们刚刚才关心完可访问性! |
讨价还价 | 我保证,如果您让我们跳过捆绑检查,我们最终将合并到三个工具提示库中 |
悲伤 | 当我尝试看看是否有效时,我应该意识到我走上了一条黑暗的道路npm install * 。 |
验收 | 我喜欢我的慢速网站。 |
仅仅证明速度很重要还不够:我们还得在情感上说服大家。天哪,要让所有人知道,如果我们的网站速度快了,会有多好。
因此,我决定创建一个重复使用我们的 API 的演示站点,但速度尽可能快。
剧透:我自己都没想到,我成功了。然后事情就变得很奇怪了。不过在讲这个故事之前,我得先讲这个故事……
目标:速度有多快?
HTTP/1.1 204 No Content
Cache-Control: max-age=999999999,immutable
这是最快的网页。你可能不喜欢,但这就是巅峰性能的体现。
这看起来似乎没什么用——当然,一个有用的页面比什么都没有还慢!——但任何添加到前端的东西都只会减慢它的速度。某些东西越是偏离网络的自然速度,就越需要付出更多努力来恢复它。
话虽如此,还是需要留出一些余地,否则我会浪费时间去微调每一个细节。你确实想知道你的内容、设计或开发选择何时开始影响你的用户。对于所有新增的功能,你都应该权衡其利弊。这就是性能预算存在的原因。
但要确定我的预算,我首先需要某种更高层次的目标。
某种更高层次的目标
🎯 速度如此之快,以至于在客户使用的最差的设备和网络上也很有趣。
- 目标设备:当地 Kroger 超市畅销的手机
- 辣椒的波布拉诺 VLE5
- 35 美元(促销价 15 美元)
- 规格:1 GB RAM、8 GB 总磁盘存储空间和 1.1 GHz 处理器。
- 目标连接:“慢速 3G”
- 400kbps带宽
- 400ms 往返时间延迟
- 当时,Google 建议测试什么,以及 WebPageTest 的“简单”配置和 Lighthouse 使用了什么
不幸的是,网络连接比“慢速 3G”预设的还要差,Kroger 超市内部的蜂窝数据就是一个例子。大型仓储式商店的架构就像法拉第笼一样,丢失大量数据包,从而消耗带宽和延迟。
最终,我选择了“慢速 3G”,因为它能够平衡美国大部分地区较快的网速和商店内的信号干扰。我请 Alex Russell 核实这篇文章时,他也提到“我们在农村地区仍然能看到类似的延迟”。
(这些设备和连接目标是高度特定于这个项目的:我带着网络分析仪走进商店,询问前台哪种手机最受欢迎,等等。我不会将它们视为“正常”基线。)
(等等,如果连接不稳定,是不是意味着您应该使用 Service Worker?)
是的,当网络状况很差时,您必须将其视为可选项,这是服务人员的工作。
我将撰写有关特殊 SW 酱料的文章(预告:离线流、导航预加载缓存摘要和关键 CSS 的前沿),但即使是最好的服务工作者对于网站的首次加载也是无关紧要的。
虽然我知道我想要什么规格,但我不知道这对我的预算意味着什么。幸运的是,其他人知道。
谷歌关于提高移动设备速度的建议
谷歌似乎知道如何解决网络性能问题,但他们从未正式认可特定的预算,因为它不可能是一刀切的。
虽然谷歌对具体预算讳莫如深,但他们的前首席性能主管亚历克斯·拉塞尔却并非如此。他撰写了一些重要信息,表明网络需要加速到何种程度才能保持相关性,而这篇文章正是我需要的:
综合起来,在理想条件下,我们对关键路径资源(CSS、JS、HTML 和数据)的粗略预算为:
- 对于没有太多 JS 的网站,170KB
- 对于使用 JS 框架构建的网站,大小为 130KB
你能负担得起吗?现实世界的性能预算
(Alex 后来更新了这些数字,但这些是我当时使用的数字。如果您有兴趣,请阅读这两个数字 - Alex 解释了我提到的那些比平常更糟糕的网络,展示了他在数字背后的工作,并且毫不掩饰地说明了究竟是什么减慢了网页速度。)
不幸的是,Alex 提到的硬件频率是 2GHz,而 Poblano 的频率是 1.1GHz。这意味着预算应该会降低到 100kB 左右,但我无法接受。为什么?
围绕分析进行工程设计
一如既往,第三方会毁掉一切。您可以看到2022 年网站的跨源字节情况,其中还不包括像 Dynatrace 这样的同源第三方。
我无法公布确切的数字,但当时的情况也好不到哪里去。除非发现反千字节,否则我得弄清楚哪些第三方必须被淘汰。当然,他们中的大多数都赚了钱,但我要证明,放弃他们也能赚到钱。
经过一番深思熟虑,我最终还是放弃了大约 138kB 的第三方 JS,我觉得公司肯定不会允许我离开它。就像往罐子里装满石头、鹅卵石和沙子的故事一样,我觉得绕过这些巨石进行开发比一开始就建一个“足够快”的网站,然后把它搞砸要容易得多。
经过一些绝望的延迟加载实验后,我发现我的代码不能超过 20kB(压缩后),以听取 Alex 的建议。
好的,20kB。接下来怎么办?
20KB 不算多。react
+react-dom
几乎是它的两倍。一个显而易见的替代方案是4KB 的 Preact,但它对组件代码或 Redux 灾难毫无帮助——而且我还需要 HTML 和 CSS!我不得不在这些显而易见的选择之外寻找其他选择。
一个网站真正需要什么?如果我回答了这个问题,其他一切都可以忽略不计了。
那么,即使你尝试过,网站也不能省略什么呢?
你可以只用 HTML 创建一个真正的网站——在 CSS 和 JS 出现之前,人们一直都是这样做的。
也许如果我在 HTML 中添加足够的CSS 就会看起来不错...并且如果我还有剩余空间,那么可以为从复杂交互中受益最多的部分添加一些激光聚焦的 JavaScript。
(是的,我看到你穿着Svelte.js衬衫。我将在下一篇文章中讨论它。)
如果你使用非常糟糕的 User-Agent访问亚马逊,它基本上会提供我刚才描述的内容:
亚马逊展示了什么Opera/9.80 (J2ME/MIDP; Opera Mini/5.1.21214/28.2725; U; ru) Presto/2.8.119 Version/11.10
。
这是保真度最低的亚马逊版本,通过skin=noskin
cookie 来显示。

看来我的计划可行,而且显然利润丰厚,亚马逊也打算这么做。看来不错,值得一试。
但每个人都知道经典的页面导航很慢!
你确定吗?我之前也这么想……
- 如果您内联 CSS 并高效生成 HTML,那么与网络往返相比,它们的开销可以忽略不计。
- 单页应用仍然需要 JSON 数据进行渲染,对吧?即使你将 JSON 数据内联到初始响应中,JSON→JavaScript→HTML 也不可能比直接跳到 HTML 部分更快。
- 在服务器上连接字符串不应该成为一个巨大的瓶颈。如果是的话,React SSR 如何证明将这些字符串两次连接到 HTML 和 Hydration 数据中是合理的?
但别轻信我的话——下次我们再看看结果如何。具体来说,我首先需要解决一个问题:如何在所有慢速数据源完成之前发送页面?
文章来源:https://dev.to/tigt/making-the-worlds-fastest-website-and-other-mistakes-56na