Astro Islands 架构:当 SSR 遇上前端框架
深入解析 Astro 的 Islands 架构——如何以零 JavaScript 起步,按需为交互组件注入脚本,兼顾首屏性能与开发体验。
摘要:Islands 架构的核心思想是「默认静态,按需交互」。本文从浏览器性能预算出发,解释为什么整页水合(hydration)是浪费,以及 Astro 如何用
astro:load、astro: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)的类型安全工作流。