Astro Integration 包实战:从官方 Starter 模板构建、联调并发布你的 Astro 集成
Astro 通过 Integration API 让第三方包能够在配置、开发服务器、构建等各个生命周期阶段扩展框架行为。本文基于 Astro 仓库中的官方集成模板 examples/integration/README.md 展开,结合模板源码与框架核心类型定义,系统讲解如何从零创建一个 Astro Integration 包:包括模板获取与项目结构、入口文件 index.ts 的工厂函数写法、package.json 的关键字段、astro:config:setup 等生命周期钩子的完整参数,以及 npm link 本地联调与 npm publish 发布的完整工作流。读完后你可以独立编写、调试并分发一个可复用、可发布的 Astro 集成包。
获取模板:一条命令创建 Integration 项目
官方模板的使用方式是在任意空目录中运行以下命令(引自 README):
npm create astro@latest -- --template integration
该模板的定位是"用于编写跨多个项目复用、或发布到 NPM 的 Astro 集成"的起点。模板本身刻意保持极简——没有 src/pages、没有 astro.config.mjs,因为 Integration 包本身不是一个网站,而是一个被网站项目以 integrations: [] 形式引入的第三方包。
项目结构:三个文件构成的最小 Integration 包
模板生成的目录结构如下(完整继承自 README):
/
├── index.ts
├── tsconfig.json
├── package.json
其中 index.ts 是 integration 的"入口点"(entry point):把集成在 index.ts 中导出,它就能被你的包所引用。下面逐文件解读这三个文件的真实内容。
入口 index.ts:导出工厂函数,返回 AstroIntegration
模板的 index.ts 全文如下:
import type { AstroIntegration } from 'astro';
export default function createIntegration(): AstroIntegration {
// See the Integration API docs for full details
// https://docs.astro.build/en/reference/integrations-reference/
return {
name: '@example/my-integration',
hooks: {
'astro:config:setup': () => {
// See the @astrojs/react integration for an example
// https://github.com/withastro/astro/blob/main/packages/integrations/react/src/index.ts
},
'astro:build:setup': () => {
// See the @astrojs/react integration for an example
// https://github.com/withastro/astro/blob/main/packages/integrations/react/src/index.ts
},
'astro:build:done': () => {
// See the @astrojs/partytown integration for an example
// https://github.com/withastro/astro/blob/main/packages/integrations/partytown/src/index.ts
},
},
};
}
模板注释中指向的参考实现就存放在当前仓库中。以 @astrojs/react 的源码 为例,其默认导出同样是"接收选项、返回 AstroIntegration"的工厂函数,并在 astro:config:setup 中调用钩子参数完成三件典型工作:
'astro:config:setup': ({ command, addRenderer, updateConfig, injectScript }) => {
// 1. 注册渲染器,让 Astro 知道用哪个框架渲染框架组件
addRenderer(getRenderer(versionConfig));
// 2. 把 Vite 插件(@vitejs/plugin-react)注入到 Astro 的 Vite 配置
updateConfig({ vite: getViteConfiguration({...}, versionConfig) });
// 3. 仅在 dev 命令下注入 fast-refresh 前导脚本
if (command === 'dev') {
const preamble = FAST_REFRESH_PREAMBLE.replace(`__BASE__`, '/');
injectScript('before-hydration', preamble);
}
},
它还在 astro:config:done 中通过 logger.warn 检测"同时启用了多个 JSX 渲染器且未设置 include/exclude"的冲突场景。这个真实例子直观展示了集成模板中三个空钩子各自能做什么:astro:config:setup 用于注册渲染器与注入 Vite 插件,构建类钩子则用于处理产物。
值得注意的细节是,模板中 name 字段写的是 '@example/my-integration',而 package.json 的包名是 @example/integration。name 主要用于 Astro 内部的日志与冲突检测,建议正式发布时统一为与包名一致,避免排查问题时产生歧义。
package.json:决定包"如何被消费"的关键字段
模板的 package.json 内容不长,但每个字段都直接影响集成包的安装与加载方式:
{
"name": "@example/integration",
"private": true,
"engines": { "node": ">=22.12.0" },
"version": "0.0.1",
"type": "module",
"exports": { ".": "./index.ts" },
"files": ["src", "index.ts"],
"keywords": ["withastro"],
"devDependencies": { "astro": "^7.2.10" },
"peerDependencies": { "astro": "^4.0.0" }
}
各字段的含义与约束:
exports: { ".": "./index.ts" }:把包的根导入指向index.ts。用户项目中import myIntegration from '@example/integration'时解析到的就是这个默认导出的工厂函数。这也是 README 强调"在index.ts中导出集成"的原因。files: ["src", "index.ts"]:限制npm publish时只把src目录与index.ts打进包体,控制发布体积;如果你的实现放在src/下,发布前需要确认files与实际目录、exports指向的文件保持一致。private: true:防止模板原样被误发布到 NPM。真正发布前必须移除该字段。peerDependencies:声明对 Astro 运行时的版本要求,由使用方的 Astro 项目提供实际版本,集成包自身不重复安装运行时。devDependencies中的 astro:仅用于开发期类型检查与本地运行,不进入依赖树。keywords: ["withastro"]:便于在 NPM 生态中被检索到。
模板的 tsconfig.json 只有一行 "extends": "astro/tsconfigs/strict",即复用 Astro 官方发布的严格 TS 配置基线,保证类型推导与 Astro 的类型系统对齐。
生命周期钩子:从核心类型定义看集成能介入哪些阶段
模板中的三个钩子只是起点。Astro 的集成 API 在核心类型定义 packages/astro/src/types/public/integrations.ts 中给出了完整的 BaseIntegrationHooks 定义,覆盖开发、同步、构建的全生命周期。摘录各钩子及其核心参数如下:
| 钩子 | 触发时机 | 关键参数(节选) |
|---|---|---|
astro:config:setup |
配置解析完成后、最终配置确定前,dev/build/preview/sync 均会触发 |
config、command('dev' | 'build' | 'preview' | 'sync')、isRestart、updateConfig、addRenderer、addWatchFile、injectScript、injectRoute、addClientDirective、addDevToolbarApp、addMiddleware、createCodegenDir、logger |
astro:config:done |
最终配置确定后 | config、setAdapter、injectTypes、logger、buildOutput('static' | 'server') |
astro:server:setup |
开发服务器创建后 | server(ViteDevServer)、toolbar、refreshContent、logger |
astro:server:start |
开发服务器开始监听后 | address(监听地址信息)、logger |
astro:server:done |
开发服务器退出时 | logger |
astro:build:setup |
每次 Vite 构建前(client/server 两轮各一次) | vite(InlineConfig)、pages(路由构建数据)、target('client' | 'server')、updateConfig、logger |
astro:build:generated |
构建产物生成后 | dir(输出目录)、routeToHeaders、logger |
astro:build:done |
构建全部完成后 | pages(已构建页面列表)、dir、assets(资源映射)、logger |
astro:route:setup |
每个路由解析时 | route(component 与 prerender)、logger |
astro:routes:resolved |
全部路由解析完成后 | routes、logger |
astro:build:ssr |
SSR 构建完成后 | manifest(序列化 SSR 清单)、middlewareEntryPoint、logger |
astro:build:start |
构建开始前 | logger、setPrerenderer |
几点从类型定义中可以确认的实现细节:
astro:config:setup的injectScript接受四种注入阶段,定义为InjectedScriptStage = 'before-hydration' | 'head-inline' | 'page' | 'page-ssr'。前两者、以及page阶段会被 Vite 处理与解析(head-inline除外,它直接内联进<head>的 script 标签),page-ssr则注入到每个 Astro 页面的 frontmatter 中。@astrojs/react在 dev 模式下注入的 fast-refresh preamble 用的就是before-hydration阶段。astro:build:setup会执行两次:一次针对target: 'client',一次针对target: 'server'。编写构建钩子时应根据target区分需要注入的 Vite 插件与产物处理逻辑。- 钩子函数可以是异步的:类型上所有钩子都允许返回
void | Promise<void>,可以安全地await文件系统或网络操作。 AstroIntegration的hooks类型允许通过& Partial<Record<string, unknown>>携带任意自定义键,即框架对未知钩子键采取宽松处理,不会因拼写错误而直接抛错——这也是编写集成时需要对钩子名做字符串校验类测试的原因。
在 Astro 项目中引用你的集成
集成包编写完成后,消费方的接入方式与其他官方集成完全一致:
// astro.config.mjs
import myIntegration from '@example/integration';
export default defineConfig({
integrations: [myIntegration()], // 工厂函数调用,可传参
});
框架在 packages/astro/src/core/create-vite.ts 等核心模块中按顺序调用用户集成与内置 Vite 插件管线,integrations 数组中的每一项都会在命令启动时被调用并合并其钩子。由于 astro:config:setup 钩子的 command 参数会区分当前命令,你的集成可以只在 dev 时注入刷新脚本、只在 build 时做产物处理。
开发与发布工作流
README 中的命令表完整继承如下,所有命令均从项目根目录的终端执行:
| 命令 | 作用 |
|---|---|
npm link |
将本包在本地注册为全局链接包;随后在任意 Astro 项目中运行 npm link my-integration 即可安装你的集成 |
npm publish |
将包发布到 NPM。发布前需确保已登录 NPM 账号 |
一个可落地的本地联调循环是:
- 在集成模板目录中运行
npm link,把@example/integration注册到本机 npm 全局链接; - 在目标 Astro 项目根目录运行
npm link @example/integration(README 中写作npm link my-integration,替换为你的包名); - 在
astro.config.mjs的integrations中引入并调用该集成,npm run dev验证行为;修改index.ts后无需重新构建,link 场景下源码即被直接解析(模板exports直接指向./index.ts,这一点对本地联调尤为友好); - 验证通过后,移除
package.json中的private: true,修正name为正式包名,再运行npm publish发布。
小结
这个仅含三个文件的 模板 覆盖了编写 Astro 集成包的全部骨架:index.ts 以工厂函数导出 AstroIntegration,package.json 用 exports/files/peerDependencies 决定包的分发形态,npm link 与 npm publish 分别支撑本地联调与正式发布。在此基础上,参考 integrations.ts 中的 BaseIntegrationHooks 完整定义和 @astrojs/react 等仓库内真实集成的实现,你可以把配置注入、渲染器注册、构建产物处理等能力逐步填充进自己的集成,形成一个可跨项目复用、可发布到 NPM 的 Astro 扩展包。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00