渲染模式
岛屿架构
tl;dr: 岛屿架构鼓励在服务器渲染的网页中创建小的、集中的交互性块。岛屿的输出是逐步增强的 HTML,并对增强方式进行了更具体的说明。不再是单个应用程序控制整个页面的渲染,而是有多个入口点。这些交互性“岛屿”的脚本可以独立地交付和水合,从而允许页面的其余部分仅为静态 HTML。
加载和处理多余的 JavaScript 会损害性能。然而,即使在主要为静态的网站中,也经常需要一定程度的交互性和 JavaScript。我们已经讨论了 静态渲染的变体,它使您能够构建试图在以下方面找到平衡的应用程序:
- 与客户端渲染 (CSR) 应用程序相当的交互性
- 与 SSR 应用程序相当的 SEO 优势。
SSR 的核心原则是,HTML 在服务器上渲染并与必要的 JavaScript 一起发送以在客户端重新水合。水合是在服务器渲染后,在客户端重新生成 UI 组件状态的过程。由于水合会带来 成本,因此 SSR 的每个变体都试图优化水合过程。这主要是通过 部分水合 关键组件或 流式传输 组件在渲染时实现。然而,在上述技术中,最终发送的净 JavaScript 仍然相同。
术语 岛屿架构 由 Katie Sylor-Miller 和 Jason Miller 推广,以描述旨在通过“交互性岛屿”减少发送的 JavaScript 数量的范式,这些岛屿可以在其他静态 HTML 之上独立交付。岛屿是一种基于组件的架构,它建议对页面进行分区,包括静态和动态岛屿。页面的静态区域是纯非交互式 HTML,不需要水合。动态区域是 HTML 和脚本的组合,能够在渲染后重新水合自身。
让我们更详细地探索岛屿架构,以及当前可用于实现它的不同选项。
动态组件岛屿
大多数页面都是静态内容和动态内容的组合。通常,一个页面由静态内容组成,并散布着一些可以隔离的交互式区域。例如;
- 博客文章、新闻文章和组织主页包含文本和图像,以及社交媒体嵌入和聊天等交互式组件。
- 电子商务网站上的产品页面包含静态产品描述和指向应用程序中其他页面的链接。图像轮播和搜索等交互式组件可在页面的不同区域使用。
- 典型的银行账户详细信息页面包含一个静态交易列表,其中过滤器提供一些交互性。
静态内容是无状态的,不会触发事件,并且在渲染后不需要水合。渲染后,动态内容(按钮、过滤器、搜索栏)必须与其事件重新连接。DOM 必须在客户端重新生成(虚拟 DOM)。这种重新生成、水合和事件处理功能会增加发送到客户端的 JavaScript。
岛屿架构促进对页面及其所有静态内容进行服务器端渲染。但是,在这种情况下,渲染后的 HTML 将包含动态内容的占位符。动态内容占位符包含自包含的组件小部件。每个小部件类似于一个应用程序,并且将服务器渲染的输出和用于在客户端水合应用程序的 JavaScript 结合在一起。
在渐进式水合中,页面的水合架构是自上而下的。页面控制各个组件的调度和水合。在岛屿架构中,每个组件都有自己的水合脚本,该脚本异步执行,独立于页面上的任何其他脚本。一个组件的性能问题不应影响其他组件。
实现岛屿
岛屿架构借鉴了不同来源的概念,并旨在将它们以最佳方式组合在一起。基于模板的静态网站生成器,如 Jekyll 和 Hugo,支持将静态组件渲染到页面。大多数现代 JavaScript 框架还支持 同构渲染,允许您使用相同的代码在服务器和客户端上渲染元素。
Jason 的帖子建议使用 requestIdleCallback()
来实现组件水合的调度方法。静态同构渲染和组件级部分水合的调度可以内置到框架中以支持岛屿架构。因此,框架应
- 支持在服务器上进行无 JavaScript 的静态页面渲染。
- 支持通过静态内容中的占位符嵌入独立的动态组件。每个动态组件都包含其脚本,并且可以在主线程空闲后使用 requestIdleCallback() 自行水合。
- 允许在服务器上进行组件的同构渲染,并在客户端进行水合,以识别两端相同的组件。
您可以使用接下来讨论的开箱即用的选项之一来实现这一点。
框架
当今的各种框架都能够支持岛屿架构。其中值得注意的是
- Marko: Marko 是一款由 eBay 开发 并 维护 的开源框架,旨在提高服务器渲染性能。它通过将流式渲染与自动部分水合相结合来支持岛屿架构。HTML 和其他静态资产在准备就绪后立即流式传输到客户端。自动部分水合允许交互式组件自行水合。水合代码仅 针对交互式组件发送,这些组件可以在浏览器中更改状态。它是同构的,Marko 编译器会根据代码运行的位置(客户端或服务器)生成优化的代码。
- Astro: Astro 是一款静态网站构建器,可以从使用其他框架(如 React、Preact、Svelte、Vue 等)构建的 UI 组件生成轻量级的静态 HTML 页面。需要客户端 JavaScript 的组件与其依赖项一起单独加载。因此,它提供了内置的部分水合功能。Astro 还可以 延迟加载 组件,具体取决于它们何时变得可见。我们在下一节中包含了一个 示例实现,它使用 Astro。
- Eleventy + Preact: Markus Oberlehner 演示了 Eleventy 的使用,Eleventy 是一款具有同构 Preact 组件的静态网站生成器,可以部分水合。它还支持延迟水合。组件本身通过声明方式控制组件的水合。交互式组件使用
WithHydration
包装器,以便它们在客户端上进行水合。
请注意,Marko 和 Eleventy 的出现时间早于 Jason 提供的岛屿定义,但包含支持岛屿架构所需的一些功能。但是,Astro 是基于该定义构建的,并且本质上支持岛屿架构。在下一节中,我们将演示将 Astro 用于前面讨论的简单博客页面示例。
示例实现
以下是我们使用 Astro 实现的示例博客页面。SamplePost 页面导入了一个交互式组件 SocialButtons。此组件通过标记包含在所需位置的 HTML 中。
1---2// Component Imports3import { SocialButtons } from '../../components/SocialButtons.js';4---56<html lang="en">7 <head>8 <link rel="stylesheet" href="/blog.css" />9 </head>1011 <body>12 <div class="layout">13 <article class="content">14 <section class="intro">15 <h1 class="title">Post title (static)</h1>16 <br/>17 <p>Post sub-title (static)</p>18 </section>19 <section class="intro">20 <p>This is the post content with images that is rendered by the server.</p>21 <Image src="https://source.unsplash.com/user/c_v_r/200x200" />22 <p>The next section contains the interactive social buttons component which includes its script.</p>23 </section>24 <section class="social">25 <div>26 <SocialButtons client:visible></SocialButtons>27 </div>28 </section>29 </article>30 </div>31 </body>32</html>
SocialButtons
组件是一个 Preact 组件,包含其 HTML 和相应的事件处理程序。
该组件在运行时嵌入到页面中,并在客户端上进行水合,以便点击事件按需发挥作用。
Astro 允许在 HTML、CSS 和脚本之间进行干净的分离,并鼓励基于组件的设计。它易于安装和开始使用此框架构建网站。
优点和缺点
岛屿架构结合了不同渲染技术的理念,例如服务器端渲染、静态网站生成和部分水合。实现岛屿的一些潜在优势如下。
- 性能: 减少发送到客户端的 JavaScript 代码量。发送的代码仅包含交互式组件所需的脚本,这远小于重新创建整个页面的虚拟 DOM 并重新水合页面上所有元素所需的脚本。JavaScript 的较小尺寸会自动对应于更快的页面加载速度和交互时间 (TTI)。
对比 Astro 与 Next.js 和 Nuxt.js 创建的文档网站,显示 JavaScript 代码减少了 83%。其他用户 也报告了 Astro 带来性能提升。
- SEO: 由于所有静态内容都在服务器端渲染,页面对 SEO 友好。
- 优先显示重要内容: 关键内容(特别是博客、新闻文章和产品页面)几乎立即提供给用户。交互的次要功能通常需要在关键内容可用后逐渐提供。
- 可访问性: 使用标准的静态 HTML 链接访问其他页面有助于提高网站的可访问性。
- 基于组件: 该架构提供了基于组件架构的所有优势,例如可重用性和可维护性。
尽管有这些优势,但该概念仍处于起步阶段。有限的支持会导致一些缺点。
- 开发人员实施 Islands 的唯一选择是使用少数可用框架之一或自行开发架构。将现有网站迁移到 Astro 或 Marko 需要额外的努力。
- 除了 Jason 的初始帖子之外,关于该想法的讨论很少。
- 新框架 声称支持 Islands 架构,这使得难以过滤哪些框架适合你。
- 该架构不适合高度交互的页面,例如社交媒体应用程序,这可能需要数千个 Islands。
Islands 架构的概念比较新,但由于其性能优势,可能会迅速发展。它强调使用 SSR 渲染静态内容,同时通过动态组件支持交互,对页面的性能影响最小。我们希望将来在这个领域看到更多参与者,并拥有更广泛的实现选项可供选择。