Chapter 01

Astro 简介:Islands 架构与零 JS 哲学

了解 Astro 的诞生背景、Islands 架构的革命性理念,以及如何构建近乎零 JavaScript 的极速网站

Astro 的诞生背景

内容网站的 JS 困境

2020 年前后,前端框架生态高度繁荣,React、Vue、Angular 几乎成为所有网站的标配。然而这类单页应用(SPA)框架在设计之初是为构建高度交互的 Web 应用(如管理后台、在线编辑器)而生的——它们把整个应用打包成 JavaScript,在浏览器中动态渲染页面。

问题在于:博客、文档站、营销页面、新闻网站等内容驱动型网站根本不需要这么重的 JS。一篇博客文章 98% 的内容是静态文字和图片,只有搜索框、评论区等极少部分需要交互。但 SPA 框架会让浏览器下载并执行几百 KB 甚至几 MB 的 JavaScript,才能渲染出用户眼前本可以直接是 HTML 的内容。

性能数据令人担忧

HTTP Archive 的研究显示,2021 年中位数的网页在移动端加载时需要执行约 400KB 的 JavaScript。过多的 JS 会导致 FID(首次输入延迟)和 LCP(最大内容绘制)等核心网络指标恶化,直接影响 SEO 排名和用户体验。

Astro 的诞生(2021)

2021 年 3 月,Fred K. Schott(曾创建 Snowpack 打包工具)发布了 Astro 的第一个公开版本。Astro 的核心理念是激进的:对于内容网站,默认不向浏览器发送任何 JavaScript

Astro 不是要取代 React 或 Vue,而是提出了一种新的渲染哲学——将服务端渲染和客户端激活(hydration)的粒度精细化到组件级别。你可以在 Astro 中使用 React 组件,但这个 React 组件默认只在服务端运行,产出静态 HTML;只有你明确标记「这个组件需要在客户端激活」时,它才会向浏览器输出 JS 代码。

Astro 关键版本时间线

Astro 1.0(2022.8)
正式发布 1.0 稳定版,确立文件路由、组件语法、多框架集成的核心模型。
Astro 2.0(2023.1)
引入内容集合(Content Collections),用 Zod 为 Markdown/MDX 文件提供类型安全查询。
Astro 3.0(2023.8)
引入 View Transitions API 支持、图片优化组件 <Image>、React Fast Refresh 支持。
Astro 4.0(2023.12)
Astro Dev Toolbar(开发工具栏)、国际化(i18n)路由内置支持、增量内容缓存。
Astro 5.0(2024.12)
Content Layer API(重构内容集合底层)、服务端岛屿(Server Islands)、静态路由类型安全。

Islands 架构详解

什么是 Islands 架构?

Islands 架构(群岛架构)这一术语由 Etsy 工程师 Katie Sylor-Miller 于 2019 年最先提出,后由 Preact 作者 Jason Miller 在 2020 年的文章《Islands Architecture》中系统阐述,Astro 则是这一理念最成熟的实现。

其核心思想是:把一个 Web 页面想象成大海中的众多孤岛。大海代表页面主体——大量静态的 HTML 内容,不需要 JavaScript,服务端直接渲染,浏览器直接展示。孤岛代表页面中少数需要交互的区域(如搜索框、购物车、图表、轮播图),这些区域才需要 JavaScript,被称为「交互岛屿」。

┌──────────────────────────────────────────────────────────┐ │ 静态 HTML(大海) │ │ ┌──────────┐ ┌──────────────┐ │ │ │ 导航栏 │ 静态文章内容 │ 搜索框 🏝️ │ │ │ │ 静态 │ 静态列表 │ (React组件) │ │ │ └──────────┘ 静态图片 │ 需要JS激活 │ │ │ └──────────────┘ │ │ ┌─────────────────┐ │ │ 静态页脚内容 │ 购物车组件 🏝️ │ 静态版权信息 │ │ │ (Vue组件) │ │ │ │ 需要JS激活 │ │ │ └─────────────────┘ │ └──────────────────────────────────────────────────────────┘ 传统 SPA:整个页面都是 JS 驱动的「大陆」 Islands:大部分是静态 HTML「大海」,少数是交互「孤岛」

Islands 架构的性能优势

传统 SPA 框架(如 Next.js 默认配置)在服务端渲染后,还需要在客户端下载并执行整个框架 + 整个页面所有组件的 JS,才能使页面「激活」变为可交互(这个过程叫做 Hydration,水合)。即使页面 90% 的内容是静态的,也要付出 100% 的 JS 开销。

Astro 的 Islands 架构实现了部分水合(Partial Hydration):只有被标记的交互岛屿才会下载和执行 JS,其余静态内容完全没有 JS 负担。在典型的博客或营销页面上,这意味着:

并行加载的额外优势

Astro 的各个交互岛屿是独立加载的——它们不共享一个 JS 运行时,每个岛屿独立初始化。这意味着一个岛屿的 JS 加载失败不会影响其他岛屿,而且多个岛屿可以并行加载,而不是像 SPA 那样顺序执行一个大的 bundle。

MPA vs SPA:范式之争

多页应用(MPA)与单页应用(SPA)

理解 Astro 的设计哲学,需要先理解 MPA 与 SPA 的根本差异:

维度 MPA(多页应用) SPA(单页应用) Astro(Islands MPA)
页面渲染 服务端生成完整 HTML 客户端 JS 动态渲染 服务端生成 HTML,岛屿客户端激活
初始 JS 体积 几乎为零 数百 KB 到数 MB 极少(仅岛屿部分)
页面导航 完整页面刷新 客户端路由,无刷新 默认页面刷新,可选 View Transitions
SEO 友好度 极佳 需要 SSR 或预渲染 极佳(天然 HTML)
适用场景 内容网站、文档 高交互应用、后台 内容网站、博客、文档、营销页

Astro 本质上是现代化的 MPA,但通过 View Transitions API 解决了传统 MPA 页面跳转时的闪烁问题,通过 Islands 架构解决了纯 MPA 无法集成组件化 UI 框架的问题。

client: 指令——岛屿激活控制

指令详解

在 Astro 中,当你想要某个框架组件(React/Vue/Svelte)在浏览器中激活为可交互的岛屿时,需要添加 client: 指令。不同的指令控制激活时机,影响性能和用户体验。

client:load
页面加载后立即激活组件。适合页面中最重要的交互元素,如顶部导航菜单。延迟效果最小,但 JS 加载优先级最高。
client:idle
等待浏览器空闲后再激活(使用 requestIdleCallback)。适合非关键交互,如侧边栏小部件、推荐内容区块。
client:visible
当组件进入视口时才激活(使用 IntersectionObserver)。适合折叠内容、页面底部组件。这是最推荐的策略,按需加载显著减少初始 JS。
client:media="query"
满足 CSS 媒体查询时才激活。适合仅在移动端或桌面端出现的组件,如移动端汉堡菜单。
client:only="react"
跳过服务端渲染,仅在客户端渲染。适合依赖 window/document 等浏览器 API 的组件。必须指定框架名称。
---
// src/pages/index.astro
import SearchBox from '../components/SearchBox.jsx';
import ShoppingCart from '../components/ShoppingCart.vue';
import HeroSection from '../components/Hero.astro';
import LazyChart from '../components/Chart.jsx';
---

<!-- .astro 组件:服务端渲染,零 JS -->
<HeroSection title="欢迎来到我的博客" />

<!-- React 搜索框:页面加载后立即激活 -->
<SearchBox client:load />

<!-- Vue 购物车:浏览器空闲时激活 -->
<ShoppingCart client:idle />

<!-- React 图表:滚动到视口内才激活 -->
<LazyChart client:visible />

<!-- 仅移动端激活的菜单 -->
<MobileMenu client:media="(max-width: 768px)" />
无 client: 指令 = 零 JS

如果你在 Astro 中使用 React/Vue 组件但不加任何 client: 指令,该组件只会在服务端执行,输出静态 HTML,完全不向浏览器输出任何 JavaScript。这意味着组件中的 onClick、useState 等客户端特性不会生效,但静态内容会正确渲染。

与 Next.js / Nuxt 的对比

设计目标差异

很多人疑惑:Astro 和 Next.js 都能做静态网站,有什么本质区别?答案在于两者的设计目标从根本上不同:

在构建博客、文档站、营销页面时,Astro 的默认设置就会输出更小的 JS 体积和更高的性能分数;而在构建高度交互的 Web 应用(如 SaaS 后台)时,Next.js 的生态和工具链会更适合。

框架 默认渲染模式 默认 JS 输出 最适合
Astro 服务端渲染 / 静态生成 极少(仅岛屿) 内容网站、博客、文档
Next.js 混合(SSR/SSG/CSR) 整个 React 运行时 全栈 Web 应用、电商
Nuxt 混合(SSR/SSG/CSR) 整个 Vue 运行时 全栈 Web 应用、内容站
Gatsby 静态生成(SSG) 整个 React 运行时 静态内容网站(较老生态)

安装与项目创建

环境要求

Astro 需要 Node.js 18.17.1+ 或 20.3.0+(推荐使用 Node 20 LTS)。如果还没安装 Node.js,推荐使用 nvm(Node Version Manager)进行管理。

创建新项目

# 方法 1:使用官方脚手架(推荐)
npm create astro@latest

# 交互式引导:
# ✔ Where should we create your new project? → ./my-blog
# ✔ How would you like to start your new project? → Use blog template
# ✔ Install dependencies? → Yes
# ✔ Initialize a new git repository? → Yes

# 方法 2:指定模板直接创建
npm create astro@latest -- --template blog
npm create astro@latest -- --template docs
npm create astro@latest -- --template minimal

# 进入项目目录并启动开发服务器
cd my-blog
npm run dev
# 访问 http://localhost:4321

项目结构预览

my-blog/
├── src/
│   ├── pages/           # 文件即路由
│   │   ├── index.astro  # 首页 → /
│   │   └── about.astro  # 关于页 → /about
│   ├── layouts/         # 页面布局模板
│   │   └── BaseLayout.astro
│   ├── components/      # 可复用组件
│   │   └── Header.astro
│   └── content/         # 内容集合(Markdown/MDX)
│       └── blog/
├── public/              # 静态资源(原样复制到输出)
│   └── favicon.svg
├── astro.config.mjs     # Astro 配置文件
├── package.json
└── tsconfig.json

基础配置文件

// astro.config.mjs
import { defineConfig } from 'astro/config';

export default defineConfig({
  // 网站 URL(部署到的域名)
  site: 'https://my-blog.com',

  // 输出模式
  output: 'static',  // 'static' | 'server' | 'hybrid'

  // 集成(框架、样式工具等)
  integrations: [],

  // 构建配置
  build: {
    // 资产文件名格式
    assets: '_astro',
  },
});

运行开发服务器

# 启动开发服务器(热更新)
npm run dev       # http://localhost:4321

# 构建生产版本
npm run build     # 输出到 dist/ 目录

# 预览生产构建
npm run preview   # 本地预览 dist/ 的内容

# 类型检查
npx astro check
Astro Dev Toolbar(开发工具栏)

Astro 4.0+ 提供了内置的开发工具栏,在浏览器底部显示。它可以高亮页面中的 Islands 组件、显示组件层级关系、检查无障碍性问题。这是理解 Islands 架构运行机制的好工具——你可以直观地看到哪些部分是「孤岛」、哪些是静态 HTML。