岛屿架构 ✔

岛屿架构鼓励在服务器渲染的网页中进行小的、集中的交互块。

岛屿的输出是逐步增强的 HTML,在增强发生的方式上具有更多的特异性。

不是单个应用程序控制整页呈现,而是有多个入口点。

这些交互性“孤岛”的脚本可以独立交付和水合,允许页面的其余部分只是静态 HTML。

加载和处理过多的 JavaScript 会影响性能。
但是,通常需要一定程度的交互性和 JavaScript,即使在主要是静态网站中也是如此。

我们已经讨论了服务器端渲染 (SSR) 的变体,这些变体使您能够构建尝试在以下各项之间找到平衡的应用程序:

  • 与客户端渲染 (CSR) 应用程序相当的交互性

  • 与 SSR 应用程序相当的 SEO 优势

SSR 的核心原则是 HTML 在服务器上呈现,并附带必要的 JavaScript,以便在客户端对其重新水合。

Rehydration 是在服务端渲染后在客户端重新生成 UI 组件状态的过程。

由于再水化是有代价的,SSR 的每个变体都试图优化再水化过程。

这主要是通过关键组件的部分水合或组件在渲染时的流来实现的。

然而,在上述技术中最终交付的网络 JavaScript 保持不变。


岛屿架构

岛屿架构一词由 Katie Sylor-Miller 和 Jason Miller 推广,用来描述一种范式,旨在减少通过交互“孤岛”传输的 JavaScript 量,这些交互“孤岛”可以在其他静态 HTML 之上独立交付。

孤岛是一种基于组件的体系结构,它建议使用静态和动态孤岛来划分页面视图。

  • 页面的静态区域是纯非交互式 HTML,不需要加水。

  • 动态区域是 HTML 和脚本的组合,能够在渲染后自我修复。

让我们通过目前可用的不同选项来更详细地探索 Islands 架构。


动态组件岛

大多数页面是静态和动态内容的组合。 通常,一个页面由静态内容和一些可以隔离的交互区域组成。 例如;

  • 博客文章、新闻文章和组织主页包含文本和图像以及社交媒体嵌入和聊天等交互式组件。

  • 电子商务网站上的产品页面包含静态产品描述和应用程序其他页面的链接。 图像轮播和搜索等交互组件可在页面的不同区域使用。

  • 一个典型的银行账户详细信息页面包含一个静态交易列表,带有提供一些交互性的过滤器。

静态内容是无状态的,不会触发事件,并且在渲染后不需要重新水化。

渲染后,动态内容(按钮、过滤器、搜索栏)必须重新连接到其事件。
DOM 必须在客户端(虚拟 DOM)重新生成。 这种再生、再水化和事件处理功能有助于将 JavaScript 发送到客户端。

Islands 架构有助于在服务器端呈现页面及其所有静态内容。
但是,在这种情况下,呈现的 HTML 将包含动态内容的占位符。 动态内容占位符包含自包含的组件小部件。
每个小部件类似于一个应用程序,并结合了服务器渲染的输出和用于在客户端上为应用程序添加水的 JavaScript。

在渐进式补水中,页面的补水架构是自上而下的。 该页面控制各个组件的调度和水合。
每个组件在 Islands 架构中都有自己的 hydration 脚本,该脚本异步执行,独立于页面上的任何其他脚本。 一个组件中的性能问题不应影响另一个。


实施岛

Island 架构借鉴了不同来源的概念,旨在将它们优化组合。
Jekyll 和 Hugo 等基于模板的静态站点生成器支持将静态组件渲染到页面。
大多数现代 JavaScript 框架还支持同构渲染,这允许您使用相同的代码在服务器和客户端上渲染元素。

Jason 的帖子建议使用 requestIdleCallback() 来实现一种用于补水组件的调度方法。
可以将组件级部分水化的静态同构渲染和调度构建到框架中以支持岛屿架构。 因此,该框架应

  1. 支持在服务器上使用零 JavaScript 静态呈现页面。

  2. 支持通过静态内容中的占位符嵌入独立的动态组件。 每个动态组件都包含它的脚本,一旦主线程空闲,就可以使用 requestIdleCallback() 将其自身水化。

  3. 允许同构渲染服务器上的组件与客户端的 hydration 以识别两端的相同组件。

可以使用下面讨论的开箱即用选项之一来实现这一点。

架构

如今,不同的框架都能够支持 Islands 架构。 其中值得注意的是

  1. Marko:

Marko 是一个由 eBay 开发和维护的开源框架,用于提高服务器渲染性能。
它通过将流式渲染与自动部分水化相结合来支持岛屿架构。
HTML 和其他静态资产一准备好就流式传输到客户端。自动部分水合允许交互式组件自行水合。
Hydration 代码仅用于交互式组件,可以更改浏览器上的状态。它是同构的,并且 Marko 编译器会根据它将运行的位置(客户端或服务器)生成优化的代码。

  1. Astro:

Astro 是一个静态站点构建器,可以从 React、Preact、Svelte、Vue 等其他框架中构建的 UI 组件生成轻量级静态 HTML 页面。
需要客户端 JavaScript 的组件与其依赖项单独加载。因此,它提供了内置的部分水合作用。
Astro 还可以延迟加载组件,具体取决于它们何时可见。我们在下一节中包含了一个使用 Astro 的示例实现。

  1. Eleventy + Preact:

Markus Oberlehner 演示了 Eleventy 的使用,这是一个静态站点生成器,具有可以部分水合的同构 Preact 组件。
它还支持懒惰的水合作用。组件本身声明性地控制组件的水合作用。交互式组件使用 WithHydration 包装器,以便它们在客户端上水合。

请注意,Marko 和 Eleventy 早于 Jason 提供的岛屿定义,但包含支持它所需的一些功能。
然而,Astro 是基于定义构建的,并且本质上支持 Islands 架构。

在下一节中,我们将演示如何在前面讨论的简单博客页面示例中使用 Astro。


示例实现

以下是使用 Astro 实现的示例博客页面。
SamplePost 页面导入了一个交互式组件 SocialButtons。 该组件通过标记包含在 HTML 中所需的位置。

Astro page (SamplePost.astro)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

---
// Component Imports
import { SocialButtons } from '../../components/SocialButtons.tsx';
---
 
<html lang="en">
 <head>
   <link rel="stylesheet" href="/blog.css" />
 </head>
 
 <body>
   <div class="layout">
     <article class="content">
       <section class="intro">
         <h1 class="title">Post title (static)</h1>
         <br/>
         <p>Post sub-title (static)</p>
       </section>
       <section class="intro">
           <p>This is the  post content with images that is rendered by the server.</p>
           <img src="https://source.unsplash.com/user/c_v_r/200x200" />
           <p>The next section contains the interactive social buttons component which includes its script.</p>
       </section>
       <section class="social">
           <div>
           <SocialButtons client:visible></SocialButtons>
           </div>
       </section>
     </article>
   </div>
 </body>
</html>

SocialButtons 组件是一个 Preact 组件,带有它的 HTML 和相应的事件处理程序。

SocialButtons component (SocialButtons.tsx)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21

import { useState } from 'preact/hooks';
 
/** a counter written in Preact */
export function SocialButtons() {
 const [count, setCount] = useState(0);
 const add = () => setCount((i) => i + 1);
 const subtract = () => setCount((i) => i - 1);
 
 return (
   <>
     <div>
       {count} people liked this post
     </div>
     <div align="right">
       <img src="/like.png" width="32" height="32" onclick={add}></img>
       <img src="/unlike.png" width="32" height="32" onclick={subtract}></img>
     </div>
   </>
 );
}

该组件在运行时嵌入到页面中,并在客户端水合,以便单击事件按需要运行。

Astro 允许 HTML、CSS 和脚本之间的清晰分离,并鼓励基于组件的设计。 使用此框架可以轻松安装和开始构建网站。


优缺点

Islands 架构结合了来自不同渲染技术的想法,例如服务器端渲染、静态站点生成和部分水化。 实施孤岛的一些潜在好处如下。

  1. 性能:

减少传送到客户端的 JavaScript 代码量。 发送的代码仅包含交互组件所需的脚本,远少于重新创建整个页面的虚拟 DOM 和重新水化页面上所有元素所需的脚本。
JavaScript 的较小尺寸自动对应于更快的页面加载和可交互时间 (TTI)。

Astro 与为 Next.js 和 Nuxt.js 创建的文档网站的比较显示 JavaScript 代码减少了 83%。 其他用户也报告了 Astro 的性能改进。

  1. SEO:由于所有静态内容都在服务器上呈现; 页面是 SEO 友好的。

  2. 优先考虑重要内容:用户几乎可以立即获得关键内容(尤其是博客、新闻文章和产品页面)。 在消费关键内容逐渐可用后,通常需要用于交互的辅助功能。

  3. 可访问性:使用标准静态 HTML 链接访问其他页面有助于提高网站的可访问性。

  4. 基于组件:该架构提供了基于组件架构的所有优点,例如可重用性和可维护性。

尽管有这些优势,但该概念仍处于起步阶段。 有限的支持导致一些缺点。

  1. 开发人员实现 Islands 的唯一选择是使用少数可用的框架之一或自己开发架构。 将现有站点迁移到 Astro 或 Marko 需要额外的努力。

  2. 除了 Jason 的初始帖子,几乎没有关于这个想法的讨论。

  3. 新框架声称支持 Islands 架构,因此很难过滤出适合您的架构。

  4. 该架构不适合高度交互的页面,例如可能需要数千个岛屿的社交媒体应用程序。

Islands 架构概念相对较新,但由于其性能优势可能会加快速度。
它强调使用 SSR 来呈现静态内容,同时通过动态组件支持交互性,同时对页面性能的影响最小。
我们希望未来在这个领域看到更多的参与者,并有更多的实施选项可供选择。



知识点