使用 JavaScript 呈现 HTML 与呈现由服务器发送的 HTML 不同,这会影响性能。要了解不同之处,以及您可以采取哪些措施来保持网站的呈现性能(尤其是在涉及互动的情况下)。
对于使用浏览器内置导航逻辑(有时称为“传统网页加载”)的网站,默认情况下,解析和呈现 HTML 的功能非常强大或“困难的导航”此类网站有时称为多页应用 (MPA)。
不过,开发者可以处理浏览器默认设置,以满足他们的应用需求。使用单页应用 (SPA) 模式的网站当然就属于这种情况,该模式会使用 JavaScript 在客户端上动态创建大部分 HTML/DOM。客户端呈现是此设计模式的名称,如果涉及的工作量过大,可能会影响网站的 Interaction to Next Paint (INP)。
本指南将帮助您权衡使用服务器发送到浏览器的 HTML 与使用 JavaScript 在客户端上创建 HTML 之间的区别,以及后者如何在关键时刻导致高互动延迟。
浏览器如何呈现服务器提供的 HTML
传统网页加载中使用的导航模式涉及在每次导航时从服务器接收 HTML。如果您在浏览器的地址栏中输入网址或点击 MPA 中的链接,就会发生以下一系列事件:
- 浏览器针对所提供的网址发送导航请求。
- 服务器以区块的形式提供 HTML 进行响应。
其中的最后一步很关键。这也是服务器/浏览器交换中最基本的性能优化之一,称为“流式传输”。如果服务器可以尽快开始发送 HTML,且没有等待整个响应到达,那么浏览器就可以在 HTML 到达时以块为单位对其进行处理。
![Chrome 开发者工具的性能面板中呈现的由服务器发送的 HTML 解析的屏幕截图。当 HTML 数据流入时,系统会在多个较短的任务中处理数据块,并以增量方式呈现。](https://web.developers.google.cn/static/articles/client-side-rendering-of-html-and-interactivity/image/a-screenshot-parsing-ht-f2443fe1b1888.png?authuser=9&hl=sr)
与浏览器中发生的大多数操作一样,解析 HTML 的操作也是在任务中进行的。当 HTML 从服务器流式传输到浏览器时,浏览器会在相应数据流的若干块分块到达时,一次一点,以优化对该 HTML 的解析。这样一来,浏览器就会在处理每个分块后定期让出主线程,从而避免了耗时较长的任务。这意味着,系统在解析 HTML 时会发生其他工作,包括向用户显示网页所需的增量呈现工作,以及处理网页的关键启动期间可能发生的用户互动。这种方法可以为网页带来更高的 Interaction to Next Paint (INP) 分数。
从中可以看出,从服务器流式传输 HTML 时,您可以免费增量解析和呈现 HTML,并自动让出到主线程。使用客户端呈现时却无法实现这一点。
浏览器如何呈现由 JavaScript 提供的 HTML
虽然对网页的每个导航请求都需要服务器提供一定数量的 HTML,但某些网站会使用 SPA 模式。这种方法通常要求服务器提供最低的 HTML 初始有效负载,但是之后客户端会使用从服务器提取的数据汇编的 HTML 填充网页的主要内容区域。后续导航 - 有时称为“软导航”— 完全由 JavaScript 处理,用新的 HTML 填充页面。
在通过 JavaScript 将 HTML 动态添加到 DOM 的情况下,在更多情况下,非 SPA 中也可能会发生客户端呈现。
创建 HTML 或通过 JavaScript 向 DOM 添加内容有以下几种常用方法:
- 借助
innerHTML
属性,您可以通过浏览器将其解析为 DOM 的字符串设置现有元素上的内容。 - 借助
document.createElement
方法,您无需使用任何浏览器 HTML 解析,即可创建要添加到 DOM 的新元素。 document.write
方法允许您将 HTML 写入文档(浏览器对其进行解析,就像第 1 种方法一样)。但是,由于多种原因,强烈建议不要使用document.write
。
![Chrome 开发者工具的性能面板中呈现的通过 JavaScript 呈现的 HTML 解析的屏幕截图。工作发生在单个冗长的任务中,该任务会阻塞主线程。](https://web.developers.google.cn/static/articles/client-side-rendering-of-html-and-interactivity/image/a-screenshot-parsing-ht-7b89c1ab03511.png?authuser=9&hl=sr)
通过客户端 JavaScript 创建 HTML/DOM 的后果可能相当显著:
- 与服务器为响应导航请求而流式传输的 HTML 不同,客户端上的 JavaScript 任务不会自动分块,这可能会导致冗长的任务阻塞主线程。这意味着,如果您在客户端上同时创建太多 HTML/DOM,则网页的 INP 可能会受到负面影响。
- 如果在启动期间在客户端上创建了 HTML,那么浏览器预加载扫描程序不会发现其中引用的资源。这无疑会对网页的 Largest Contentful Paint (LCP) 产生负面影响。虽然这并非运行时性能问题(而是在获取重要资源时出现网络延迟问题),但您不希望自己网站的 LCP 因规避这项基本的浏览器性能优化而受到影响。
您可采取哪些措施来解决客户端呈现对性能的影响
如果您的网站非常依赖于客户端呈现,并且发现字段数据中的 INP 值很低,那么您可能想知道客户端呈现是否与该问题有关。例如,如果您的网站是一个 SPA,则您的现场数据可能会揭示造成大量呈现工作的互动。
无论原因是什么,下面都列出了一些潜在的原因,你可以从中探索,让一切恢复正常。
从服务器提供尽可能多的 HTML
如前所述,默认情况下,浏览器以非常高效的方式处理来自服务器的 HTML。它会以可避免耗时长任务的方式拆分 HTML 的解析和渲染,并优化主线程总时间量。这样可以减少总阻塞时间 (TBT),而且 TBT 与 INP 密切相关。
您可能会依赖前端框架来构建网站。如果是,请确保在服务器上呈现组件 HTML。这将限制您的网站所需的初始客户端呈现量,并且应该会带来更好的体验。
- 对于 React,您需要使用 Server DOM API 在服务器上呈现 HTML。但请注意:传统的服务器端呈现方法使用同步方法,这可能会导致首字节时间 (TTFB) 以及首次内容绘制 (FCP) 和 LCP 等后续指标更长。尽可能确保使用适用于 Node.js 的流式传输 API 或其他 JavaScript 运行时,以便服务器尽快开始将 HTML 流式传输到浏览器。Next.js(一个基于 React 的框架)默认提供许多最佳实践。除了在服务器上自动呈现 HTML 之外,它还可以为不会根据用户背景(如身份验证)而变化的网页静态生成 HTML。
- 默认情况下,Vue 还会执行客户端渲染。不过,与 React 一样,Vue 也可以在服务器上渲染组件 HTML。请尽可能利用这些服务器端 API,或者考虑为您的 Vue 项目采用更高级别的抽象,以便更轻松地实现最佳实践。
- 默认情况下,Svelte 在服务器上呈现 HTML,不过,如果您的组件代码需要访问浏览器专用的命名空间(例如
window
),那么您可能无法在服务器上呈现该组件的 HTML。请尽可能探索其他方法,以免造成不必要的客户端渲染。SvelteKit(Svelte as Next.js to React)在 Svelte 项目中尽可能嵌入许多最佳实践,以免在只使用 Svelte 的项目中出现潜在的陷阱。
限制在客户端创建的 DOM 节点数量
当 DOM 很大时,渲染它们所需的处理往往会增加。无论您的网站是成熟的 SPA,还是由于 MPA 的交互而将新节点注入现有 DOM,请考虑使这些 DOM 尽可能保持小巧。这将有助于减少在客户端呈现期间显示该 HTML 所需的工作量,有望有助于降低您网站的 INP。
考虑流式传输 Service Worker 架构
这是一种先进技术,可能并不适用于所有用例,但通过它,您可以将 MPA 转变为网站,使网站在用户从一个页面导航到另一个页面时感觉网站能够立即加载。您可以使用 Service Worker 在 CacheStorage
中预缓存网站的静态部分,同时使用 ReadableStream
API 从服务器提取网页的 HTML 的其余部分。
如果成功使用此方法,并非在客户端上创建 HTML,但从缓存中即时加载部分内容会让您感觉您的网站正在快速加载。采用这种方法的网站有点像是 SPA,但不存在客户端渲染问题的缺点。还可减少您向服务器请求的 HTML 数量。
简而言之,流式传输 Service Worker 架构并不会取代浏览器的内置导航逻辑,而是向其中添加内容。如需详细了解如何通过 Workbox 实现这一点,请参阅使用信息流加快多页面应用的速度。
总结
网站接收和呈现 HTML 的方式会影响性能。当您依靠服务器发送网站正常运行所需的全部(或大部分)HTML 时,您可以免费获得很多好处:增量解析和呈现,以及自动让出给主线程以避免耗时较长的任务。
客户端 HTML 呈现会引入许多在许多情况下都是可以避免的潜在性能问题。不过,由于每个网站的要求,这并非完全可以避免的情况。为减少因过度呈现客户端网站而导致的潜在耗时较长任务,请确保尽可能多地从服务器发送网站的 HTML,尽可能缩减必须在客户端上呈现的 HTML 的 DOM 大小,并考虑采用替代架构来加快将 HTML 交付给客户端的速度,同时利用浏览器为从服务器加载的 HTML 提供的增量解析和呈现功能。
如果您尽可能减少网站的客户端呈现,您不仅能提高网站的 INP,还能改善其他指标,例如 LCP、TBT,在某些情况下甚至可能还包括 TTFB。
主打图片来自 Unspin,Maik Jonietz。