为什么选择 Astro:群岛架构与零 JS 默认
本教程共 56 篇 · 第 2 篇 · 更新于 2026-08-07 · 约 9 分钟阅读
本节目标:搞懂群岛架构到底是什么,客户端岛屿和服务端岛屿有什么不同,以及 Astro 凭什么能比别的框架快那么多。
上一章我们说到,Astro 的招牌是「群岛架构」。这一章把它拆开揉碎讲清楚。理解了它,你就理解了 Astro 为什么快、为什么对 SEO 友好。
群岛架构这个名儿从哪来
「组件岛屿」(component island)这个词,最早是 Etsy 的前端架构师 Katie Sylor-Miller 在 2019 年提出来的。2020 年,Preact 的作者 Jason Miller 把它写成文章、推广开来。
Jason Miller 自己的原话大意是:在服务器上把 HTML 页面渲染出来,然后在那些高度动态的区域周围预留「占位 / 插槽」,这些区域再在客户端被「水合」成一个个独立的小部件,复用服务器一开始渲染好的 HTML。
Note术语小卡片:水合(hydration)指的是——页面已经被服务器渲染成 HTML 了,浏览器再加载 JavaScript,把这段 HTML「激活」成可交互的组件。与之相对,部分水合(partial hydration)就是「只激活页面里需要交互的那几块」,而不是整页激活。
什么是「岛屿」
在 Astro 里,岛屿就是「静态 HTML 海洋中的一块增强型 UI 组件」。
具体来说分两种:
- 客户端岛屿(client island):一个带交互的 JavaScript 组件,它和页面其余部分分开水合。比如一个图片轮播、一个搜索框。
- 服务端岛屿(server island):一个 UI 组件,它的动态内容是在服务端单独渲染的,不是浏览器里跑。比如已登录用户的头像。
两者都把「耗时的活」按组件为单位独立处理,好让首屏尽快出来。
可以这样想象:一张页面大部分是安静的静态 HTML「海面」,只有少数几块像「小岛」一样浮在上面、会动。岛屿之间各跑各的,互不拖累。
默认零 JS 是怎么做到的
Astro 有个很「硬」的默认行为:每个 UI 组件都会被自动渲染成纯 HTML 和 CSS,客户端 JavaScript 全部被剥掉。
看一段最简单的情况:
---
import MyReactComponent from "../components/MyReactComponent.jsx";
---
<MyReactComponent />
你写了一个 React 组件,但没说要它交互。Astro 就只把它渲染成静态 HTML,JavaScript 一点不带。这看着有点「粗暴」,但正是它保住速度的关键——开发者很难「手滑」把多余的 JS 发到用户浏览器里。
想要让某个组件真正能动起来,你得加一个客户端指令(client directive)。
客户端指令:你说了算
把一个静态组件变成「会动的岛屿」,只需加 client:* 指令。Astro 会自动帮你打包好对应的 JavaScript。
---
import MyReactComponent from "../components/MyReactComponent.jsx";
---
<!-- 这个组件现在在页面上是可交互的了 -->
<!-- 页面其他部分依旧是静态的 -->
<MyReactComponent client:load />
常见的客户端指令有这几个:
client:load:页面一加载就立刻水合。适合首屏就要交互的东西,比如顶部导航菜单。client:idle:等浏览器「闲下来」再水合。适合页面靠下的、不急的内容。client:visible:等这个组件滚进可视区域才水合。适合轮播、弹窗、长列表这类看不见就不加载的东西。client:media:当某个 CSS 媒体查询条件满足时才水合。适合「只在特定屏幕宽度才出现」的元素,比如移动端的折叠侧边栏、窄屏才显示的菜单。它接收一段媒体查询字符串,例如client:media="(max-width: 600px)"。client:only:跳过服务端渲染,只在浏览器里水合。慎用,因为它会伤害 SEO,而且你必须显式告诉 Astro 这个组件用的是什么框架(值写成client:only="react"这样),否则 Astro 在构建时不知道该加载哪套运行时。
Tip把
client:visible用在屏幕底下的组件上,是个很实用的优化。用户没滚到那里,JS 就不下载,省下的都是真金白银的流量。
为什么这样能快
群岛架构带来的好处,主要有两个层面。
第一,JavaScript 总量骤减
页面大部分成了静态 HTML,只有你点名的组件才带 JS。而 JavaScript 恰恰是「按字节算最慢」的资源之一,能省则省。
第二,岛屿可以并行加载
假设页面里有个优先级低的「图片轮播」岛屿,它不必挡住优先级高的「页头」岛屿。两块各加载各的、各水合各的。结果就是:页头立刻能点,底下重的轮播慢慢来,互不等待。
更妙的是,你能精确控制每个组件的加载时机。那个轮播很重?给它挂个 client:visible,用户不看就永远不加载。
Note关键结论:在 Astro 里,「哪些组件要在浏览器里跑」是由你显式指定的。Astro 只水合页面真正需要的部分,剩下全是静态 HTML。这正是一句口号背后的真相——客户端岛屿是 Astro「默认就快」的秘密。
服务端岛屿:动态内容不拖后腿
前面说了客户端岛屿,这里补上服务端岛屿。
有些内容动态但没必要在浏览器算,比如「当前登录用户的头像」「给这个用户特供的优惠」。这种活交给服务端更合适。
给任意 Astro 组件加 server:defer 指令,它就变成服务端岛屿:
---
import Avatar from "../components/Avatar.astro";
---
<Avatar server:defer />
它的作用是:把昂贵的服务端代码从主渲染流程里挪开。页面的主体内容可以先带着「占位内容」(比如一个通用头像)立刻渲染出来,等岛屿自己的内容就绪再替换。
这样做有两个好处:
- 外层的壳和主内容能被更激进地缓存,整体更快。
- 访客体验好。岛屿渲染通常极快,在浏览器还没画完页面时就加载完了;即便要等一下,你也能显示自定义的兜底内容,避免页面「跳一下」。
电商商品页是个典型例子:商品主图、描述很少变,但页头头像、专属优惠、用户评价是动态的。用服务端岛屿,访客先看到最重要的商品,个性化部分随后补上。
Tip服务端岛屿不依赖特定服务器,从 Docker 里的 Node 服务到各家无服务器平台都能跑,迁移起来很省心。
什么时候群岛反而不合适
群岛不是万能药。下面这些场景,它可能不是最佳选择:
- 整页都是实时交互:比如在线画板、多人协作表格、实时对战游戏。整页都「会动」,用完整 SPA 框架反而更顺手。
- 大量跨岛屿共享状态:Astro 的岛屿彼此隔离。要让两个岛屿共享数据,得靠 Web API(比如 localStorage、自定义事件)。如果你到处都要共享状态,复杂度会冒出来。
但对九成以上的网站——官网、仪表盘、内容平台、后台面板——群岛架构都占优。核心就一句:能少发 JavaScript 就少发,用户的设备会感谢你。
小结
这一章我们看清了 Astro 快的真正原因:
- 群岛架构把页面主体变静态 HTML,只在交互处放岛屿。
- 客户端指令
client:*让你精确控制「哪些组件在浏览器跑、什么时候跑」。 - 服务端岛屿
server:defer让动态内容不拖慢首屏。 - 部分水合 + 并行加载,是「默认就快」的底气。
下一章,我们站在「选技术栈」的视角,看看 Astro 到底适合做什么、不适合做什么。