多框架集成的设计理念
为什么 Astro 支持多框架?
Astro 的设计哲学是「UI 框架无关」。它不强迫你选择某个框架,而是让你在同一个项目中混合使用 React、Vue、Svelte 甚至 Preact。这在以下场景特别有用:
- 从一个框架逐步迁移到另一个框架(渐进式迁移)
- 复用已有的组件库(如已有大量 React 组件)
- 团队成员熟悉不同框架
- 为特定功能选择最合适的框架(如用 Svelte 写动画)
安装框架集成
# 使用 astro add 自动安装并配置(推荐)
npx astro add react
npx astro add vue
npx astro add svelte
npx astro add solid
# 或者手动安装
npm install @astrojs/react react react-dom
npm install @astrojs/vue vue
// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import vue from '@astrojs/vue';
import svelte from '@astrojs/svelte';
export default defineConfig({
integrations: [react(), vue(), svelte()],
});
在 Astro 中使用框架组件
React 组件
// src/components/Counter.jsx
import { useState } from 'react';
export default function Counter({ initialCount = 0 }) {
const [count, setCount] = useState(initialCount);
return (
<div className="counter">
<button onClick={() => setCount(c => c - 1)}>-</button>
<span>{count}</span>
<button onClick={() => setCount(c => c + 1)}>+</button>
</div>
);
}
在 .astro 中使用,带 client: 指令
---
import Counter from '../components/Counter.jsx';
import SearchBox from '../components/SearchBox.jsx';
import VueModal from '../components/Modal.vue';
import SvelteAnimation from '../components/Anim.svelte';
---
<!-- 不加 client: → 服务端渲染,无JS,useState 不工作 -->
<Counter />
<!-- client:load → 立即激活,useState 工作 -->
<Counter client:load initialCount={5} />
<!-- client:visible → 滚动到视口才激活(节省初始 JS) -->
<SearchBox client:visible />
<!-- Vue 组件 -->
<VueModal client:idle />
<!-- Svelte 组件 -->
<SvelteAnimation client:load />
Nano Stores:跨框架状态共享
为什么需要 Nano Stores?
当你的页面有多个岛屿(一个 React 计数器、一个 Vue 购物车),它们可能需要共享状态。由于各岛屿是独立的 JS 运行时,不能共享 React/Vue 的响应式状态。Nano Stores 是一个极小的状态管理库(300 字节),专为多框架场景设计。
npm install nanostores @nanostores/react @nanostores/vue
// src/stores/cart.ts
import { atom, computed } from 'nanostores';
export const cartItems = atom<Array<{ id: string; name: string; qty: number }>>([]);
export const cartCount = computed(cartItems, items =>
items.reduce((sum, item) => sum + item.qty, 0)
);
export function addToCart(item: { id: string; name: string }) {
const current = cartItems.get();
const existing = current.find(i => i.id === item.id);
if (existing) {
cartItems.set(current.map(i => i.id === item.id ? { ...i, qty: i.qty + 1 } : i));
} else {
cartItems.set([...current, { ...item, qty: 1 }]);
}
}
// React 组件使用 Nano Store
import { useStore } from '@nanostores/react';
import { cartCount, addToCart } from '../stores/cart';
export default function AddButton({ product }) {
const count = useStore(cartCount);
return (
<button onClick={() => addToCart(product)}>
加入购物车(当前 {count} 件)
</button>
);
}
对于 Astro Islands 的跨框架状态,Nano Stores 是最佳选择:体积极小(不增加额外 JS),框架无关(React/Vue/Svelte/Solid 都有适配器),API 简单直观。Redux 等重量级状态库不适合岛屿架构。
client: 指令详解
client:media="(max-width: 768px)"——仅在移动端激活汉堡菜单。桌面端完全不加载该组件的 JS。Vue 组件集成示例
<!-- src/components/Dropdown.vue -->
<template>
<div class="dropdown">
<button @click="open = !open">{{ label }}</button>
<ul v-if="open">
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>
<script setup lang="ts">
defineProps<{ label: string; items: Array<{ id: number; name: string }> }>()
const open = ref(false)
</script>
---
import Dropdown from '../components/Dropdown.vue';
const menuItems = [
{ id: 1, name: '首页' },
{ id: 2, name: '关于' },
];
---
<Dropdown client:idle label="菜单" items={menuItems} />
Server Islands:Astro 5 的延迟服务端渲染
Server Islands 是 Astro 5 引入的新特性,允许在静态页面中嵌入需要服务端动态渲染的组件——无需整个页面都开启 SSR。原理是:页面主体静态生成,标记了 server:defer 的组件在浏览器请求时异步获取其服务端渲染结果(通过独立的 HTTP 请求),然后替换占位符。
---
// src/pages/dashboard.astro
// 此页面可以是纯静态的!
import UserStats from '../components/UserStats.astro';
import RecommendedPosts from '../components/RecommendedPosts.astro';
---
<!-- 页面主体:静态生成,CDN 缓存 -->
<main>
<h1>仪表盘</h1>
<!-- Server Island:个性化内容,按需服务端渲染 -->
<UserStats server:defer>
<!-- fallback slot:渲染期间显示骨架屏 -->
<div slot="fallback" class="skeleton">加载中...</div>
</UserStats>
<!-- 另一个 Server Island -->
<RecommendedPosts server:defer>
<div slot="fallback">
{[1,2,3].map(() => <div class="skeleton-card"></div>)}
</div>
</RecommendedPosts>
</main>
---
// src/components/UserStats.astro
// 此组件在请求时服务端执行(每次用户访问)
import { getSession } from '../lib/auth';
const session = await getSession(Astro.request);
const stats = await db.user.getStats(session?.userId);
---
<!-- 这段 HTML 是动态渲染的,包含用户个性化数据 -->
<div class="stats-card">
<p>欢迎回来,{session?.user.name}!</p>
<p>您有 {stats.unreadMessages} 条未读消息</p>
</div>
如果页面上只有少量动态内容(如用户名、购物车数量、个性化推荐),用 Server Islands 比整页 SSR 更高效:页面外壳走 CDN 缓存(毫秒级),动态部分独立请求。只有当大部分页面内容都需要动态化时,才应该开启整页 SSR。这是 Astro 5 性能优化的核心思路。
server:defer 要求项目配置了 SSR 适配器(Vercel、Netlify、Node 等)——即使页面本身是静态预渲染的(output: 'static' 或 output: 'hybrid'),也需要适配器来处理 Server Islands 的独立请求。如果没有安装适配器,使用 server:defer 会报构建错误。
本章小结
- Astro 框架无关哲学:同一项目中可混用 React/Vue/Svelte/Solid,每个岛屿独立运行;适合渐进式迁移或团队混合技术栈场景。
- client: 指令控制激活时机:load(立即)→ idle(浏览器空闲)→ visible(进入视口)→ media(满足媒体查询);默认不加 client: 则无 JS,是真正的静态渲染。
- 跨岛屿状态共享:各 Islands 是独立的 JS 运行时,不能共享 React state;用 Nano Stores(300字节)实现跨框架的全局状态,API 与各框架的响应式系统无缝集成。
- client:only 的必要性:依赖 window/localStorage 的代码无法在 SSR 中运行;必须用 client:only="react" 完全跳过服务端渲染,同时需指定框架以正确加载运行时。
- 多框架对 bundle 的影响:每个框架的运行时(React ~40KB, Vue ~30KB)都会被打包到客户端。使用多框架时应权衡 JS 体积;Svelte 编译为原生 JS,无运行时负担。
- Server Islands(server:defer):Astro 5 新特性,在静态页面中嵌入按需服务端渲染的动态组件;页面外壳走 CDN 缓存,动态部分独立异步请求;用 slot="fallback" 提供加载占位;兼顾静态性能与个性化内容,是整页 SSR 的轻量替代方案。