Astro 建站实战 · 第 7 章:交互岛屿
/ 5 min read
第 7 章 · 交互岛屿
学习目标
- 理解岛屿 = 「服务端渲染的静态 HTML + 按需水合的交互孤岛」。
- 掌握五种
client:*指令的取舍。 - 知道岛屿的三条边界:不能嵌 Astro 组件、props 要可序列化、Astro 组件本身不可水合。
概念
第 1 章说过:默认零 JS,交互的局部才是「岛」。落地分两步:
npx astro add react(或 svelte/vue/solid)——安装集成。- 在
.astro模板里像用普通组件一样用框架组件,加client:*指令声明何时水合。
动手实践:五种 client 指令
---import Like from "../components/Like.jsx";import Comments from "../components/Comments.jsx";import ThemePicker from "../components/ThemePicker.jsx";---<Like client:load /> {/* 立即水合:首屏交互关键件 */}<Comments client:idle /> {/* 浏览器空闲再水合:次要交互 */}<Comments client:visible /> {/* 滚进视口才水合:评论区标配 */}<ThemePicker client:media="(max-width: 768px)" /> {/* 仅匹配媒体查询时:移动端专属件 */}<MyWidget client:only="react" /> {/* 跳过服务端渲染,纯客户端 */}默认(不加指令)框架组件也会服务端渲染成静态 HTML——没有 JS 但内容可见;client:only 是唯一跳过服务端渲染的。
三条边界,文档写得很直白:
- 岛屿里不能 import Astro 组件。Astro 的静态内容想进岛屿,用 slot 从外面传:
<MySidebar><AstroThing slot="header" /></MySidebar>。 - props 必须可序列化:函数传不过去(服务端/客户端两个世界),React 的 render-props 模式失效——用命名 slot 替代。
- Astro 组件加
client:*会报错:它没有运行时,无水合可言。需要交互就用<script>(作用域是全局的,每个组件只执行一次)。
踩坑提示
- 岛屿的 JS 成本按「框架运行时 + 组件代码」计,同一框架多个岛共享一份运行时——所以同页尽量用同一框架。
client:visible的组件若初始渲染依赖视口计算,注意水合前后的一帧闪烁。- 大多数「我需要一点交互」的场景(主题切换、折叠面板、回到顶部)其实一个原生
<script>+ 几行 DOM 操作就够,不需要整个岛屿——先问这问题值多少 KB。
对照本站
本站是「岛屿克制度」的极端样本:全站零框架岛屿,三个交互件全是手写的:
- ThemeToggle.astro / ThemeProvider.astro:
<theme-toggle>自定义元素 + 几十行 script,读写localStorage并切data-theme。 - Header.astro:移动端菜单是一个
<mobile-button>自定义元素,十行以内。 - Search.astro:搜索是构建期的 Pagefind 索引 + 一个
<pagefind-config>元素加载其 UI——搜索这种重交互也可以不引入框架运行时。
为什么这么抠?第 1 章的立场一以贯之:内容站的每个 KB 都该花在读者可感知的地方。什么时候该用真岛屿?看练习 3。
练习
npx astro add react,写一个<Like />组件(点击 +1),分别用默认、client:load、client:visible渲染三次,对比网络面板里的 JS 数量。- 给岛屿通过命名 slot 传入一段 Astro 渲染的静态内容,并在 React 里用
props.header接住。 - 假设要做「文章打分」组件(五颗星、需要状态、需要动画),决策一下:原生 script 还是 React 岛屿?写出理由。
遗留问题
站点的骨架、内容、交互都齐了。但搜索引擎怎么找到你?订阅者怎么订阅你?分享到社交平台的卡片图从哪来?下一章:SEO 与分发。