跳转到内容

服务端群岛

服务器岛允许你按需独立渲染动态或个性化的“岛屿”,而不会牺牲页面其余部分的性能。

这意味着你的访问者将更快地看到页面中最重要的部分,并允许你的主要内容被更积极地缓存,从而提供更快的性能。

服务器岛是一个普通的服务器端渲染的 Astro 组件,它被指示延迟渲染,直到其内容可用。

你的页面将立即使用任何指定的回退内容作为占位符进行渲染。然后,组件自身的内容在客户端被获取,并在可用时显示。

安装了适配器以执行延迟渲染后,将 server:defer 指令添加到页面上的任何组件,以将其变成自己的岛。

src/pages/index.astro
---
import Avatar from '../components/Avatar.astro';
---
<Avatar server:defer />

这些组件可以使用适配器在按需渲染的页面中做任何你通常会做的事情,例如获取内容和访问 cookie。

src/components/Avatar.astro
---
import { getUserAvatar } from '../sessions';
const userSession = Astro.cookies.get('session');
const avatarURL = await getUserAvatar(userSession);
---
<img alt="User avatar" src={avatarURL} />

提供给服务器岛组件的 Props 必须是可序列化的:能够被转换成适合通过网络传输或存储的格式。此外,Astro 并不会序列化每一种可序列化的数据结构。因此,可以作为 props 传递给服务器岛的内容存在一些限制。

值得注意的是,函数不能传递给标有 server:defer 的组件,因为它们无法被序列化。带有循环引用的对象也无法序列化。

支持以下 prop 类型:普通对象、numberstringArrayMapSetRegExpDateBigIntURLUint8ArrayUint16ArrayUint32ArrayInfinity

当在组件上使用 server:defer 属性来延迟其渲染时,你可以使用附带的名为 "fallback" 的插槽(slot)来“插入”默认的加载内容。

你的回退内容最初会在页面加载时与页面的其余部分一起渲染,并在组件内容可用时被替换。

要添加回退内容,请在你传递给服务器岛组件的子元素(其他组件或 HTML 元素)上添加 slot="fallback"

---
import Avatar from '../components/Avatar.astro';
import GenericAvatar from '../components/GenericAvatar.astro';
---
<Avatar server:defer>
<GenericAvatar slot="fallback" />
</Avatar>

这种回退内容可以是诸如以下的东西:

  • 一个通用的头像,而不是用户自己的头像。
  • 占位符 UI,例如自定义消息。
  • 加载指示器,例如旋转器。

服务器岛的实现主要发生在构建时,此时组件内容被替换为一个小型脚本。

每个标记为 server:defer 的岛屿都会被分割成自己的特殊路由,脚本在运行时会获取该路由。当 Astro 构建你的网站时,它会省略该组件,并在其位置注入一个脚本以及你标记为 slot="fallback" 的任何内容。

当页面在浏览器中加载时,这些组件将被请求到一个特殊的端点,该端点会渲染它们并返回 HTML。这意味着用户将立即看到页面最关键的部分。在动态岛屿加载之前,回退内容将短暂可见。

每个岛屿都独立于其他部分加载。这意味着一个较慢的岛屿不会延迟你其他个性化内容的可用性。

这种渲染模式被设计为可移植的。它不依赖于任何服务器基础设施,因此它可以与你拥有的任何主机一起工作,从 Docker 容器中的 Node.js 服务器到你选择的无服务器提供商。

服务器岛的数据通过 GET 请求检索,将 props 作为加密字符串在 URL 查询中传递。这允许使用标准的 Cache-Control 指令,通过 Cache-Control HTTP 标头缓存数据。

然而,出于实际原因并为了避免导致拒绝服务问题,浏览器将 URL 限制在最大长度 2048 字节。如果你的查询字符串导致 URL 超过此限制,Astro 将改为发送一个在请求体中包含所有 props 的 POST 请求。

POST 请求不会被浏览器缓存,因为它们用于提交数据,可能会导致数据完整性或安全问题。因此,你项目中任何现有的缓存逻辑都将失效。尽可能只向你的服务器岛传递必需的 props,并避免发送整个数据对象和数组,以保持你的查询短小。

在大多数情况下,你的服务器岛组件可以通过像普通组件一样传递 props来获取有关渲染它的页面的信息。

然而,服务器岛在页面请求之外的独立上下文中运行。在服务器岛组件中,Astro.urlAstro.request.url 都会返回一个类似于 /_server-islands/Avatar 的 URL,而不是浏览器中当前页面的 URL。此外,如果你正在预渲染页面,你将无法访问诸如查询参数之类的信息来作为 props 传递。

要从页面的 URL 访问信息,你可以检查 Referer 标头,它将包含正在浏览器中加载该岛屿的页面的地址。

---
const referer = Astro.request.headers.get('Referer');
const url = new URL(referer);
const productId = url.searchParams.get('product');
---

Astro 使用密码学来加密传递给服务器岛的 props,以保护敏感数据免于意外泄露。这种加密依赖于一个在每次构建时生成并嵌入到服务器包中的新的随机密钥。

大多数部署主机会自动处理前端和后端的同步。但是,如果你正在使用滚动部署、多区域托管或缓存包含服务器岛的页面的 CDN,你可能需要一个恒定的加密密钥。

在具有滚动部署的环境中(例如 Kubernetes),你的前端资源(加密 props)和你的后端函数(解密 props)可能会暂时使用不同的密钥,或者当 CDN 仍在提供使用旧密钥构建的页面时,传递给你服务器岛的加密 props 将无法解密。

在这些情况下,请使用 Astro CLI 生成一个可复用的、编码后的加密密钥,并将其设置为构建环境中的环境变量。

终端窗口
astro create-key

使用此值来配置 ASTRO_KEY 环境变量(例如在 .env 文件中),并将其包含在你的 CI/CD 或主机的构建设置中。这确保了在生成的包中始终复用相同的密钥,从而使加密和解密保持同步。

贡献 社区 赞助