← 全部文章

Astro Islands 架构:当 SSR 遇上前端框架

深入解析 Astro 的 Islands 架构——如何以零 JavaScript 起步,按需为交互组件注入脚本,兼顾首屏性能与开发体验。

摘要:Islands 架构的核心思想是「默认静态,按需交互」。本文从浏览器性能预算出发,解释为什么整页水合(hydration)是浪费,以及 Astro 如何用 astro:loadastro:idle 等指令精确控制脚本加载时机。

问题的起点:交互是昂贵的

每个现代前端框架都在做同一件事:把组件树渲染成 DOM,然后水合——在浏览器端重新执行组件逻辑,绑定事件。对于一个以内容为主的页面,这意味着用户为了一两个按钮,下载并执行了几百 KB 的框架运行时。

整页水合:  框架运行时 (≈ 100KB) + 页面组件 (≈ 60KB) + 路由 (≈ 30KB)
Islands:   只有交互组件才会加载脚本

什么是 Islands

Islands 架构(由 Etsy 的 Katie Sylor-Miller 提出,Preact 作者 Jason Miller 系统化)把页面视为「静态海洋中的交互孤岛」:

  • 静态区域:HTML + CSS,无任何 JavaScript
  • 交互孤岛:独立的组件,各自加载自己的脚本
// 一个 Island:只有它自己会水合
import Counter from '../components/Counter.tsx';

<Counter client:load />;

Astro 中的实践

Astro 提供五个 client: 指令控制水合时机:

指令 时机 适用场景
client:load 页面加载后立即 首屏关键交互
client:idle 浏览器空闲时 非关键组件
client:visible 元素进入视口 折叠区域组件
client:media 满足媒体查询 移动端专属菜单
client:only 仅客户端渲染 强依赖浏览器 API 的组件
---
import ThemeToggle from '../components/ThemeToggle.astro';
---

<!-- 只有用户接近它时才加载 -->
<ThemeToggle client:visible />

每个 Island 之间互不共享运行时状态,也没有服务端传递的「水合数据」负担——这正是它与 Next.js 整页 SSR 的关键区别。

数据流:把状态保持在 HTML 里

Islands 架构下,服务端到客户端的数据传递通过 props 序列化 完成:

// 服务端渲染时传入 props,水合时直接复用
<SearchBox
  initialQuery={query}
  results={results.map((r) => ({ id: r.id, title: r.title }))}
  client:idle
/>

Astro 会把 props 序列化为 <script type="application/json">,水合时读取,而不是重新请求数据。这让初始 HTML 完整、可索引、无闪烁。

什么时候不要用 Islands

Islands 不是银弹。以下场景建议换用其他方案:

  • 整页都是高频交互(如在线文档编辑器)——SPA 或整页水合更合适
  • 需要全局状态共享(如复杂的跨组件状态管理)——Islands 的隔离模型会让状态传递变得繁琐
  • SEO 不重要的内部工具——可以直接省掉静态渲染的成本

小结

  • 内容优先的页面用 默认静态,交互组件用 client: 指令按需水合
  • client:visible / client:idle 把非首屏脚本推迟
  • 通过 props 序列化传递初始状态,保持 HTML 完整

这套模式的收益很直接:首屏 JavaScript 可以降到几 KB,Lighthouse 性能分轻松超过 95。下一篇我们将讨论如何在 Islands 之上搭建内容集合(content collections)的类型安全工作流。