作为架构的 Web 性能
为什么将性能视为后期的优化阶段会失败,以及如何设计默认情况下就很快的系统。
目录
现代 web 开发中最常见的性能失败模式是将速度视为在项目结束时需要修复的 bug。
团队花费数月时间为一个文档网站构建一个庞大的 React 应用程序,却无视不断增长的 bundle 大小。当网站上线且移动端性能极差时,他们试图通过添加记忆化(memoization)、复杂的缓存层和延迟加载边界来“优化”它。
这很少起作用。你无法通过优化来摆脱一个从根本上就很笨重的架构。性能不是一个清理步骤;它是你的渲染模型和你要求浏览器执行多少 JavaScript 的结构性后果。
水合税的代价
在过去的十年里,构建前端的默认心智模型是单页应用程序 (SPA)。为了使 SPA 加载得更快,业界采用了服务器端渲染 (SSR)。
但是 SSR 引入了水合(Hydration)税。
水合是浏览器下载 HTML,然后下载 JavaScript,接着执行该 JavaScript 以重新附加事件监听器和重建状态树的过程。在水合期间,主线程被阻塞。页面看起来准备好了,但是如果用户点击一个按钮,什么也不会发生。
设备现实
开发人员通常在具有千兆光纤的 M 系列 MacBook 上评估性能。这产生了一种虚假的确定性。
在 3G 网络上的廉价 Android 设备上,水合税是灾难性的。解析和执行 500KB 的 JavaScript 在开发者的笔记本电脑上只需几毫秒,但在中端手机上可能需要几秒钟。在这些秒数里,设备是冻结的。
对性能危机的架构回应
如果不加区分地对整个页面进行水合是问题所在,那么解决方案就是选择性执行。业界针对这个问题产生了三种截然不同的架构回应。
渲染模型比较
以下是现代渲染模型如何解决水合税的方法。
| 模型 | 工作原理 | 优点 | 缺点 |
|---|---|---|---|
| 完全水合 (标准 Next/Nuxt) | 服务器渲染 HTML。客户端加载所有 JS 并重新水合整个页面。 | 统一的心智模型;容易构建复杂的全局状态。 | 沉重的 CPU 成本。移动端上的可交互时间 (TTI) 慢。 |
| 群岛架构 (Astro) | 页面是静态 HTML。只有特定的、被标记的组件才会加载 JS 并水合。 | 零基础 JS。对于内容网站有无与伦比的性能。 | 开发者必须手动声明交互边界。 |
| 可恢复性 (Qwik) | 服务器将事件处理程序序列化为 HTML。客户端“恢复”而无需运行组件代码。 | 瞬间交互。无论应用大小如何,水合税为零。 | 非正统的心智模型;与框架编译器紧密耦合。 |
| 服务器组件 (RSC) | 服务器执行组件并将专有的有效负载发送到客户端。 | 直接在 UI 代码中访问后端;较小的客户端 bundle。 | 需要 Node/Edge 运行时;复杂的缓存语义。 |
实例分析:不要水合什么
为了构建高性能的系统,你必须培养一种直觉,知道什么不需要是交互式的。
示例 1:文档页面 (应保持静态)
你正在构建一个像这样的一篇文章页面。它有 2000 字的文本,一些代码块,以及一个侧边栏导航菜单。
糟糕的架构: 在标准的 SPA 中渲染这个。浏览器将整篇文章作为 JSON 有效负载下载,加上 React 运行时,加上路由器,然后在客户端构建 DOM 元素。 良好的架构: 将其渲染为静态 HTML。唯一需要的 JavaScript 是一个用来切换移动端导航菜单的微小脚本。
示例 2:电子商务产品页面 (混合)
你有一个产品页面,带有一个静态描述,一个图片轮播,以及一个更新全局购物车计数器的“添加到购物车”按钮。
糟糕的架构: 水合整个页面,以便“添加到购物车”按钮可以与标题对话。浏览器浪费 CPU 周期去水合页脚和静态产品描述。 良好的架构 (群岛): 产品描述和页脚是静态 HTML。图片轮播是一个岛。 “添加到购物车”按钮是另一个岛。它们通过轻量级的自定义事件或一个微小的共享 store (如 Nano Stores) 进行通信,而不会唤醒页面的其余部分。
内容优先系统的决策规则
如果你正在构建出版物、营销网站或知识库,请采用以下启发式方法:
- 默认为 HTML: 从零 JavaScript 开始。对添加的每一个字节的 JS 都要求有正当理由。
- 隔离交互性: 如果你在一篇文档页面上需要一个复杂的计算器,把它构建成一个岛。不要让文章去支付计算器的性能成本。
- 避免仅在桌面端评估: 如果没有在 DevTools 中将你的 CPU 限制为 4 倍减速并且将网络限制为 Fast 3G,永远不要签署渲染策略。
- 为 90% 的场景构建架构: 如果你的网站 90% 是阅读材料,10% 是交互式表单,请选择为阅读优化的架构 (如 Astro),而不是为复杂状态优化的架构 (如完整的 SPA)。
你总是可以为一个静态页面添加一个交互式的岛,但你很难从一个 SPA 中剥离水合税。
相关指南和术语
加深您的理解:
- 为内容优先与应用优先的产品选择堆栈 — 这如何应用于框架选择。
- 前端架构到底是什么 — 边界如何影响性能。
- MDX、内容集合与作为架构的内容 — 实现模式。