Agent Developer Roadmap
前端框架

Astro:默认 HTML、Islands 与按需水合

最后核验:2026-06-30|适用版本:Astro 5–7(示例锁定 7.0.3)|前置知识:HTML、服务端渲染、任一组件框架

Astro 的内容优先约束

Astro 默认在构建或服务端把组件渲染为 HTML,不向浏览器发送组件运行时;只有带 client:* 指令的 UI framework 组件成为 Island(交互岛)。它适合内容占主导、交互边界清楚的站点。高度交互、共享客户端状态遍布页面的应用可能更适合单一客户端框架或对应 Meta-framework。

“默认零 JavaScript”是默认行为,不是不可突破的保证。每个 Island 的框架运行时、props 和依赖都会进入客户端产物,仍需检查构建结果。

.astro 组件与服务端模板

.astro 文件的 frontmatter 在服务端执行,模板输出 HTML。它可以读取文件、内容集合和私有环境,但传给 Island 的 props 会序列化到客户端。脚本、数据库对象和密钥不能跨过该边界。

---
import catalog from '../../../contracts/technologies.json';
import Catalog from '../components/Catalog.svelte';
import { parseTechnologyEntries } from '../lib/model';
const entries = parseTechnologyEntries(catalog as unknown);
---
<Catalog entries={entries} client:load />

Astro 7 使用新的 Rust 编译器,标记纠错更严格,空白处理更接近 JSX。升级时应把不闭合和依赖旧编译器自动重排的模板当真实错误修正。Astro 7

.astro 组件由 --- 包围的组件脚本和模板组成。组件脚本在构建期或每次服务端请求中执行,绝不作为该组件的运行时自动发送到浏览器;模板允许 HTML、组件、表达式、条件与数组映射。Astro.props 接收调用者输入,Astro.paramsAstro.urlAstro.request 则来自当前路由上下文。

---
interface Props {
  title: string;
  entries: readonly { id: string; title: string }[];
}

const { title, entries } = Astro.props;
if (entries.length === 0) {
  return Astro.redirect('/empty');
}
---

<section aria-labelledby="catalog-title">
  <h1 id="catalog-title">{title}</h1>
  <ul>
    {entries.map((entry) => <li><a href={`/entries/${entry.id}`}>{entry.title}</a></li>)}
  </ul>
</section>

<style>
  section { container-type: inline-size; }
  @container (width > 40rem) { ul { columns: 2; } }
</style>

表达式默认转义,数组会展开成相邻节点;set:html 直接注入 HTML,必须只接受已经在可信边界净化的内容。组件样式默认作用域化,编译器给选择器与节点附加 scope 标记;它不是 Shadow DOM,继承属性、CSS 自定义属性与 is:global 规则仍可跨组件。<slot /> 组合调用者内容,具名 slot 只决定服务端模板放置位置,不自动建立客户端组件关系。

页面内普通 <script> 是显式客户端脚本。无 attribute 的脚本由 Astro 处理、打包并去重;带 is:inline 的脚本原样内联且每出现一次执行一次。少量导航增强、复制按钮或 Web Component 用平台脚本即可,不必为一个事件引入 UI 框架 Island。脚本在 View Transitions/客户端导航下是否重新执行必须按生命周期验证,不能默认等同整页刷新。

从源码到 HTML 与客户端 chunk

Astro 7 的编译和构建链可追踪为:Rust .astro parser 读取组件脚本与模板,生成服务端渲染模块和样式信息;Vite 8/Rolldown 建立依赖图、转换 UI 框架组件并分块;静态模式运行这些服务端模块,为每条路径写 HTML;按需模式由 Adapter 把模块变成目标运行时 handler;带 client:* 的组件另外产生浏览器模块与水合元数据。

静态页面的组件脚本只在构建时执行;按需页面在请求时执行。把 Date.now()、远程请求或用户信息放入脚本,其新鲜度由渲染模式决定。构建产物中没有组件运行时不等于没有 JavaScript:显式 <script>、框架 Island、View Transitions 和第三方脚本都可能发送代码,必须从 dist/ 与 Network 证明。

Islands 与水合边界

服务端先输出完整 HTML;浏览器只下载被标记 Island 的代码并水合该子树。Island 之间默认不共享组件树,跨岛通信应使用 URL、服务器、Web 标准事件或明确状态边界,而不是建立隐藏全局总线。

如果交互需要包围整页,单个巨大 Island 虽然合法,但会退化成普通客户端应用;此时应重新评估 Astro 是否仍减少复杂度。

Island 的服务端输出包含可立即阅读的 HTML以及告诉 Astro“哪个框架、哪个模块、哪些 props、何时水合”的边界信息。调度条件满足后,浏览器下载对应 framework renderer 与组件 chunk,在该根节点水合。多个相同框架 Island 可以共享产物 chunk,但每个 Island 仍有独立组件根、状态与调度时机。

.astro 组件不能像 React/Vue 父组件那样直接持有所有 Island 的响应式状态。跨岛同步的最小选项是 URL、表单/服务器、DOM CustomEvent 或经过明确评估的共享 store。URL 适合可分享筛选,服务器适合持久事实,CustomEvent 适合同页短暂通知;全局 store 会把独立边界重新耦合,应只在确有跨岛实时状态时采用。

传给 Island 的 props 会序列化进页面/水合载荷。数据库模型、函数、私钥和无关大字段不能传;即使 DOM 当前没有显示,访问页面的人仍可读取序列化数据。服务端与客户端框架的首次 render 还必须一致:随机数、时区、客户端宽度和不稳定排序会产生水合差异。

Server Islands 不是 Client Islands

Server Island 用 server:defer 把个性化或慢速服务端组件从页面主渲染中分离。页面壳和 fallback 先返回,浏览器随后请求 Island endpoint,服务端渲染片段并替换 fallback;组件逻辑仍不在浏览器运行。它适合购物车摘要、用户头像等不能静态共享但不应阻塞整页的片段。

---
import AccountMenu from '../components/AccountMenu.astro';
---

<AccountMenu server:defer>
  <span slot="fallback" aria-busy="true">正在读取账户…</span>
</AccountMenu>

Server Island 增加一次请求、fallback 替换和 props 重放协议;敏感数据仍需在 Island 请求中重新认证授权,不能信任初次页面生成的隐藏参数。需要按钮交互时,片段内部仍可包含 Client Island。静态内容或同一请求即可快速得到的公开数据不要拆 Server Island,额外网络往返反而更慢。

client: 指令的调度语义

client:load 尽快水合关键交互;client:idle 等待浏览器空闲;client:visible 在元素接近视口时水合;client:media 按媒体查询;client:only 跳过服务端渲染。选择依据是交互何时必须可用,不是追求最晚加载。

指令适用约束代价或风险
client:load首屏立即交互更早占用主线程
client:idle次要但页面内交互空闲前不可用
client:visible屏外复杂组件观察与延迟水合
client:media特定媒体才需要条件变化需验证
client:only无法服务端渲染无 HTML 基础结构、可能布局跳动

知识目录的搜索是页面主任务,因此使用 client:load;文章导航和标题留在 Astro HTML,不进入 Island。Framework components

client:visible 基于 IntersectionObserver,可用 rootMargin 提前水合,避免元素刚出现才开始下载;client:idle 可配置 timeout,防止繁忙页面永远不可交互;client:media 的查询条件后来变化时也要验证初始化/卸载行为。client:only="svelte" 必须明确 renderer,因为服务端没有渲染信息,并会牺牲内容初始版本和可能造成布局偏移。

水合策略先是产品正确性:菜单、搜索、同意按钮等用户看到即可操作的控件不能为追求低 JavaScript 延迟到不确定 idle;屏外图表适合 visible;仅桌面存在的重控件可考虑 media。性能证据应同时记录 JS 传输、主线程、水合完成时间和用户首次交互,不用“指令更晚”替代测量。

内容集合与运行时验证

Content Collections 为本地或远程内容建立 loader 和 schema,可生成类型并在构建期验证。它适合 Markdown/MDX 与结构化内容,不替代运行时 API 的信任边界。远程内容仍要处理网络、认证、缓存和字段错误。

集合条目通过稳定 id/slug 形成路由。schema 变化会影响构建和消费代码,迁移时先更新验证,再更新模板;不要用类型断言绕过损坏内容。Content Collections

Astro 7 的 build-time collection 在 src/content.config.ts 用 loader 获取数据,schema 在进入内容存储前验证;glob() 读取 Markdown/MDX 等目录,file() 读取 JSON/YAML 等单文件。getCollection/getEntry 查询存储,render(entry) 得到可渲染 Content。类型来自已验证 schema,但 loader 自身仍要处理网络失败、重复 ID、删除同步和缓存。

import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';

const articles = defineCollection({
  loader: glob({ base: './src/content/articles', pattern: '**/*.{md,mdx}' }),
  schema: z.object({
    title: z.string().min(1).max(100),
    description: z.string().max(240),
    publishedAt: z.coerce.date(),
    draft: z.boolean().default(false),
    tags: z.array(z.string()).default([]),
  }),
});

export const collections = { articles };

Build-time loader 在构建时更新内容,适合版本化文档;live collection 在请求时通过 getLiveEntry/getLiveCollection 获取新数据,适合频繁变化的远程内容,但每次请求的延迟、认证、限流和故障会进入页面路径。把股票价格放 build-time collection 会陈旧,把永不变化的文档放 live loader 会增加运行成本。

Markdown/MDX 是代码和内容的组合边界。仓库内受信作者可以使用组件,远程不可信 MDX 不能直接编译执行;富文本 HTML 仍需净化。图片路径、canonical、slug 和引用条目应由 schema/loader 校验,避免构建到最后阶段才出现断链。

文件路由、动态路径与渲染模式

src/pages 文件路径生成 URL:index.astro 对应目录根,[id].astro 是单动态参数,[...slug].astro 捕获剩余路径,.ts/.js 页面可导出 endpoint。默认 output: 'static' 在构建时生成 HTML;安装 Adapter 后可以按需渲染,并通过 prerender 对单路由选择。

静态动态路由必须用 getStaticPaths() 枚举 params 和 props:

---
import { getCollection, render } from 'astro:content';

export async function getStaticPaths() {
  const posts = await getCollection('articles', ({ data }) => !data.draft);
  return posts.map((post) => ({
    params: { id: post.id },
    props: { post },
  }));
}

const { post } = Astro.props;
const { Content } = await render(post);
---
<article><h1>{post.data.title}</h1><Content /></article>

按需动态路由直接从 Astro.params 查数据,不使用 getStaticPaths,不存在时返回 404。static 适合对所有访客相同且更新随发布的内容;按需 SSR 适合用户/请求相关或必须实时的数据;混合站点只让必要路由按需。预渲染无法读取真实访问者 cookie,按需路由则需要容量、超时和请求隔离。

Endpoint 导出 GETPOST 等函数并返回标准 Response。静态模式下 GET endpoint 可在构建期输出文件;运行时 API 需要 Adapter 和 prerender = false。路径参数、query、body 和 header 都是不可信输入,响应状态、Content-Type、Cache-Control 要明确。页面在服务端需要同仓数据时优先调用共享纯服务函数,没必要经自己的 HTTP endpoint 绕一圈。

路由、Actions 与 Adapter

文件路由支持静态页面、动态参数和端点。Actions 处理类型安全服务端写入,但必须执行运行时验证、授权和幂等;内容页没有写需求时不引入。Adapter 决定按需渲染、平台运行时和部署能力,默认静态输出不需要服务端 Adapter。

Action 定义在 src/actions/index.tsdefineAction 可通过 Zod 验证 JSON 或 form input,客户端、HTML form 或 Astro 脚本可调用同一服务端函数。类型安全减少调用胶水,不能证明调用者可信;身份来自服务器 session/locals,资源授权和事务在 handler 内执行。

import { ActionError, defineAction } from 'astro:actions';
import { z } from 'astro/zod';

export const server = {
  renameEntry: defineAction({
    accept: 'form',
    input: z.object({
      id: z.string().regex(/^[a-z0-9-]+$/),
      title: z.string().trim().min(1).max(80),
    }),
    handler: async (input, context) => {
      const user = await requireUser(context);
      const updated = await renameOwnedEntry({ ...input, userId: user.id });
      if (!updated) throw new ActionError({ code: 'NOT_FOUND' });
      return { id: updated.id };
    },
  }),
};

HTML form 可在无自定义客户端框架时提交 Action;POST/Redirect/GET、防重复提交和字段错误回显仍需设计。客户端禁用按钮不能阻止重放,创建/支付需要幂等键或唯一约束。Action body 大小、文件类型、CSRF Origin 检查和错误信息都属于部署安全契约。

Middleware 的 onRequest(context, next) 包围页面与 endpoint,可设置每请求 locals、认证会话、request ID 和响应头;预渲染页面在构建时也会运行 middleware,此时没有真实用户。locals 是请求级传递,不要把当前用户写模块全局。授权仍在具体 Action/endpoint/页面数据读取处执行,middleware 只做共享解析或早期拒绝。

Astro 7 Advanced Routing 通过 src/fetch.ts 和标准 fetch(request) 入口显式编排 Astro、其他后端和中间件顺序。它解决确实需要控制整个请求流水线的应用,不是普通站点必需配置;错误顺序可能让 Actions 绕过认证、日志漏记或外部代理吞掉 Astro 路由。采用时为每条分支写未匹配、异常、取消和安全测试。

Astro 7 稳定 route caching,并引入高级路由入口。缓存仍要说明键、寿命、失效和平台 provider;不为八项本地 JSON 增加缓存配置。

Route cache 面向按需渲染响应。配置 provider 后,页面用 Astro.cache.set({ maxAge, swr, tags }),endpoint/middleware 用 context cache;routeRules 可为路径集中配置。maxAge 是新鲜窗口,swr 允许陈旧响应在后台再生;tag/path invalidation 映射到 provider 的失效能力。

export default defineConfig({
  cache: { provider: memoryCache() },
  routeRules: {
    '/articles/[...slug]': { maxAge: 300, swr: 60 },
  },
});

缓存公开文章前要把 locale、preview 状态等变化维度纳入路由/键;带 cookie 的账户页默认不缓存。内存 provider 每进程独立,serverless 多实例无法全局一致;平台 CDN provider 可能在服务器执行前直接命中。开发模式 cache API 是 no-op,必须 build + preview 或目标平台验证命中、陈旧再生、失效、源站失败和并发 stampede。Route caching

知识目录示例的运行证据

Astro 示例.astro 页面验证共享数据并输出页面,只为 Svelte Catalog 加载客户端 JavaScript。禁用 JavaScript 后,服务端渲染的标题和目录骨架仍存在;交互增强需要水合。

运行效果Astro 知识目录运行效果
新窗口打开

正在加载示例……

pnpm --filter @roadmap/astro-slice test
pnpm --filter @roadmap/astro-slice preview --port 4208

输入:访问 http://127.0.0.1:4208/,查看页面源代码与 Network,搜索 islands 并选择两项。

预期结果:模型测试与静态生产构建通过;源代码包含完整目录,客户端只加载一个 Svelte Island,URL 恢复查询与选择。

构建产物、调试与安全

构建日志和 dist/ 回答哪些页面预渲染、哪些脚本发送到客户端;Network 与 Coverage 检查 Island 代码;浏览器 Performance 检查水合时机。不要以 Lighthouse 单分数代替真实用户交互和可访问性验证。

Astro 模板默认转义表达式;set:html 和框架原始 HTML API 会绕过保护。服务端私有环境变量不能传入 Island props。验收标准是能说明每个 Island 为何需要水合、何时水合、发送哪些数据和运行时。

性能排查应先确认工作发生在哪一阶段:构建慢看 loader、Markdown/MDX、页面数量和 Vite 插件;服务器慢看按需路由、上游请求和缓存;浏览器慢看 Island chunk、框架运行时、第三方脚本、图片和布局。Astro 的默认 HTML 只能减少一类客户端成本,不能自动优化数据库、图片或 CSS。

  1. astro build 的路由输出确认预渲染/按需边界,并记录构建页面数与最慢内容 loader。
  2. dist/ manifest 和 Network 确认每个 Island 实际带来的框架、组件和 props,不凭组件文件数估算。
  3. 用 Performance 记录 client:load/visible/idle 何时水合、首次交互是否在水合前发生、主线程是否被第三方脚本占用。
  4. 图片使用正确尺寸、格式、srcset/sizes 和 LCP 优先级;内容优先站点常见瓶颈是媒体而非框架代码。
  5. 缓存优化同时测命中率、新鲜度、再生延迟和错误污染,不能用开发模式结论。
现象根因候选证据与修正
HTML 有控件但点击无效Island 尚未水合或 chunk 失败看 Network/console 与 hydration 调度;关键控件改用更早指令
相同框架被发送多份integration/版本或分块边界不一致查 manifest 和 lockfile,统一 renderer/版本
构建时数据正常、线上陈旧页面被预渲染或 route cache 未失效检查 route 输出、cache headers/tag 和发布流程
水合后内容跳变服务端与客户端初值不同、client:only 无初始版本使用确定性 props,保留 SSR,固定 fallback 尺寸
私有数据出现在源代码传入 Island props 或错误页/内联脚本裁剪 DTO,秘密只留服务端,审计 HTML 与 payload

可访问性不能等水合补救:服务端 HTML 就应有正确标题层级、landmark、label、链接和按钮语义;fallback 应表达 loading 状态且替换后管理焦点。禁用 JavaScript 的检查用于证明内容初始版本,不代表所有业务必须无 JS 完成,但核心导航和信息应符合所选渐进增强承诺。

部署时静态 host 只需正确提供 HTML/assets/404 与缓存头;按需 Adapter 还要证明 runtime API、环境变量、cookies、流、body 上限、origin/Host 处理和日志。Node、serverless、edge 的文件系统、连接复用和缓存 provider 不同,生产配置应锁定具体 Adapter,不能以 astro dev 作为平台证据。

重大编译器重构、缓存变化与安全修复

Astro 5–7 的架构演进集中在内容层、服务端边界和编译链:Content Layer/Collections 统一本地与远程内容,Actions 提供服务端写入口,Server Islands/路由缓存增加动态能力,Astro 7 改用 Rust 编译器并升级 Vite 8、稳定高级路由与平台无关 route cache。Rust 编译器对错误标记和空白的处理更严格;迁移应先修正非法模板,再比较生成 HTML、Island props、脚本顺序和 CSS scope,不能用字符串替换维持旧容错。

变化主要收益高风险边界
新 Rust 编译器更快转换、统一 parser非法 HTML、空白、slot 与表达式边界改变产物
Server Islands/Actions动态片段与服务端写入props 重放、body 上限、认证授权和 CSRF
Route cache标准 max-age/SWR、tag/path 失效、provider个性化响应误缓存、provider 能力差异、错误缓存污染
Advanced Routing自定义请求流水线中间件顺序、Host/路径信任、Adapter 映射

安全升级不能只看 astro 主包。2024 的 CVE-2024-56140 修复 security.checkOrigin CSRF 绕过(4.16.17);2025 的 GHSA-xf8x-j4p2-f749 修复按需渲染 /_image 可代理未授权远程图片(Astro 5.13.2、4.16.19 与相应 Node Adapter)。2026-06 的 CVE-2026-54299 是预渲染 404/500 错误页获取中的高危 Host header SSRF,Astro <=6.4.4 受影响,6.4.6 修复;Astro 7 应继续使用最新 patch,因为同期还有 slot name/属性名 XSS、Server Island body DoS 与缓存错误处理公告。Astro Security AdvisoriesHost SSRFCSRF 绕过

回归测试应覆盖伪造 Host、非法/大小写变化的标签与属性名、恶意 slot、超大 Server Island 请求、未授权图片域,以及带 cookie 的 route cache。Adapter 必须证明它如何校验 origin、限制请求体、提供缓存 provider 和失效;“开发服务器正常”不能证明目标平台安全。

Astro 官方资料

  • Astro Islands:服务端 HTML 与交互岛模型;核验于 2026-06-30。
  • Client directives:水合调度;核验于 2026-06-30。
  • Routing:文件路由与端点;核验于 2026-06-30。
  • Adapters:按需渲染与部署环境;核验于 2026-06-30。
在 GitHub 上编辑此页

本页目录