Skip to content

Astro 适配器 API

Astro 旨在轻松部署到任何云提供商以进行 SSR(服务器端渲染)。这种能力是由 integrations 适配器提供的。请参阅 SSR 指南 了解如何使用现有适配器。

¥Astro is designed to make it easy to deploy to any cloud provider for SSR (server-side rendering). This ability is provided by adapters, which are integrations. See the SSR guide to learn how to use an existing adapter.

¥What is an adapter

适配器是一种特殊的 integration,它为服务器端渲染提供入口点。适配器有两件事:

¥An adapter is a special kind of integration that provides an entrypoint for server-side rendering. An adapter does two things:

  • 实现特定于主机的 API 来处理请求。

  • 根据主机约定配置构建。

¥Building an Adapter

适配器是 integration,可以执行集成可以执行的任何操作。

¥An adapter is an integration and can do anything that an integration can do.

适配器必须在 astro:config:done 钩子中调用 setAdapter API,如下所示:

¥An adapter must call the setAdapter API in the astro:config:done hook like so:

my-adapter.mjs
export default function createIntegration() {
return {
name: '@matthewp/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@matthewp/my-adapter',
serverEntrypoint: '@matthewp/my-adapter/server.js',
supportedAstroFeatures: {
staticOutput: 'stable'
}
});
},
},
};
}

传入 setAdapter 的对象定义为:

¥The object passed into setAdapter is defined as:

interface AstroAdapter {
name: string;
serverEntrypoint?: string;
previewEntrypoint?: string;
exports?: string[];
args?: any;
adapterFeatures?: AstroAdapterFeatures;
supportedAstroFeatures?: AstroFeatureMap;
}
export interface AstroAdapterFeatures {
/**
* Creates an edge function that will communicate with the Astro middleware.
*/
edgeMiddleware: boolean;
/**
* SSR only. Each route becomes its own function/file.
*/
functionPerRoute: boolean;
}
export type SupportsKind = 'unsupported' | 'stable' | 'experimental' | 'deprecated';
export type AstroFeatureMap = {
/**
* The adapter is able to serve static pages
*/
staticOutput?: SupportsKind;
/**
* The adapter is able to serve pages that are static or rendered via server
*/
hybridOutput?: SupportsKind;
/**
* The adapter is able to serve SSR pages
*/
serverOutput?: SupportsKind;
/**
* The adapter can emit static assets
*/
assets?: AstroAssetsFeature;
};
export interface AstroAssetsFeature {
supportKind?: SupportsKind;
/**
* Whether this adapter deploys files in an environment that is compatible with the library `sharp`
*/
isSharpCompatible?: boolean;
/**
* Whether this adapter deploys files in an environment that is compatible with the library `squoosh`
*/
isSquooshCompatible?: boolean;
}

属性是:

¥The properties are:

  • 名称:适配器的唯一名称,用于日志记录。

  • serverEntrypoint:服务器端渲染的入口点。

  • 导出:与 createExports 结合使用时的命名导出数组(如下所述)。

  • adapterFeatures:启用适配器必须支持的特定功能的对象。这些功能将改变构建的输出,适配器必须实现正确的逻辑来处理不同的输出。

  • supportedAstroFeatures:Astro 内置功能图。这使得 Astro 能够确定适配器无法或不愿意支持哪些功能,从而可以提供适当的错误消息。

¥Server Entrypoint

Astro 的适配器 API 尝试与任何类型的主机一起工作,并提供了一种灵活的方式来符合主机 API。

¥Astro’s adapter API attempts to work with any type of host, and gives a flexible way to conform to the host APIs.

¥Exports

一些无服务器主机希望你导出函数,例如 handler

¥Some serverless hosts expect you to export a function, such as handler:

export function handler(event, context) {
// ...
}

使用适配器 API,你可以通过在 serverEntrypoint 中实现 createExports 来实现此目的:

¥With the adapter API you achieve this by implementing createExports in your serverEntrypoint:

import { App } from 'astro/app';
export function createExports(manifest) {
const app = new App(manifest);
const handler = (event, context) => {
// ...
};
return { handler };
}

然后在你调用 setAdapter 的集成中,在 exports 中提供此名称:

¥And then in your integration, where you call setAdapter, provide this name in exports:

my-adapter.mjs
export default function createIntegration() {
return {
name: '@matthewp/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@matthewp/my-adapter',
serverEntrypoint: '@matthewp/my-adapter/server.js',
exports: ['handler'],
});
},
},
};
}

¥Start

有些主机希望你自己启动服务器,例如通过监听端口。对于这些类型的主机,适配器 API 允许你导出 start 函数,该函数将在运行打包脚本时调用。

¥Some hosts expect you to start the server yourselves, for example by listening to a port. For these types of hosts, the adapter API allows you to export a start function which will be called when the bundle script is run.

import { App } from 'astro/app';
export function start(manifest) {
const app = new App(manifest);
addEventListener('fetch', event => {
// ...
});
}

该模块用于渲染通过 astro build.0 预构建的页面。Astro 使用标准 要求响应 对象。具有不同请求/响应 API 的主机应在其适配器中转换为这些类型。

¥This module is used for rendering pages that have been prebuilt through astro build. Astro uses the standard Request and Response objects. Hosts that have a different API for request/response should convert to these types in their adapter.

import { App } from 'astro/app';
import http from 'http';
export function start(manifest) {
const app = new App(manifest);
addEventListener('fetch', event => {
event.respondWith(
app.render(event.request)
);
});
}

提供以下方法:

¥The following methods are provided:

app.render(request: Request, options?: RenderOptions)
Section titled app.render(request: Request, options?: RenderOptions)

该方法调用与请求匹配的 Astro 页面,渲染它,并向 响应 对象返回 Promise。这也适用于不渲染页面的 API 路由。

¥This method calls the Astro page that matches the request, renders it, and returns a Promise to a Response object. This also works for API routes that do not render pages.

const response = await app.render(request);

app.render() 方法接受强制性的 request 参数和可选的 RenderOptions 对象(用于 addCookieHeaderclientAddresslocalsrouteData)。

¥The app.render() method accepts a mandatory request argument, and an optional RenderOptions object for addCookieHeader, clientAddress, locals, and routeData.

是否自动将 Astro.cookie.set() 编写的所有 cookie 添加到响应标头。

¥Whether or not to automatically add all cookies written by Astro.cookie.set() to the response headers.

设置为 true 时,它们将作为逗号分隔的键值对添加到响应的 Set-Cookie 标头中。你可以使用标准 response.headers.getSetCookie() API 单独读取它们。设置为 false(默认)时,cookie 只能从 App.getSetCookieFromResponse(response) 获得。

¥When set to true, they will be added to the Set-Cookie header of the response as comma separated key-value pairs. You can use the standard response.headers.getSetCookie() API to read them individually. When set to false(default), the cookies will only be available from App.getSetCookieFromResponse(response).

const response = await app.render(request, { addCookieHeader: true });

客户端 IP 地址将在页面中作为 Astro.clientAddress 提供,在 API 路由和中间件中作为 ctx.clientAddress 提供。

¥The client IP address that will be made available as Astro.clientAddress in pages, and as ctx.clientAddress in API routes and middleware.

以下示例读取 x-forwarded-for 标头并将其作为 clientAddress 传递。此值作为 Astro.clientAddress 提供给用户。

¥The example below reads the x-forwarded-for header and passes it as clientAddress. This value becomes available to the user as Astro.clientAddress.

const clientAddress = request.headers.get("x-forwarded-for");
const response = await app.render(request, { clientAddress });

context.locals object 用于在请求的生命周期内存储和访问信息。

¥The context.locals object used to store and access information during the lifecycle of a request.

下面的示例读取名为 x-private-header 的标头,尝试将其解析为对象并将其传递给 locals,然后可以将其传递给任何 中间件函数

¥The example below reads a header named x-private-header, attempts to parse it as an object and pass it to locals, which can then be passed to any middleware function.

const privateHeader = request.headers.get("x-private-header");
let locals = {};
try {
if (privateHeader) {
locals = JSON.parse(privateHeader);
}
} finally {
const response = await app.render(request, { locals });
}

如果你已经知道渲染路径,请提供 routeData 的值。这样做将绕过对 app.match 的内部调用来确定渲染路由。

¥Provide a value for routeData if you already know the route to render. Doing so will bypass the internal call to app.match to determine the route to render.

const routeData = app.match(request);
if (routeData) {
return app.render(request, { routeData });
} else {
/* adapter-specific 404 response */
return new Response(..., { status: 404 });
}

此方法用于确定请求是否符合 Astro 应用的路由规则。

¥This method is used to determine if a request is matched by the Astro app’s routing rules.

if(app.match(request)) {
const response = await app.render(request);
}

你通常可以在不使用 .match 的情况下调用 app.render(request),因为如果你提供 404.astro 文件,Astro 会处理 404。如果你想以不同的方式处理 404,请使用 app.match(request)

¥You can usually call app.render(request) without using .match because Astro handles 404s if you provide a 404.astro file. Use app.match(request) if you want to handle 404s in a different way.

¥Allow installation via astro add

astro add 命令 允许用户轻松地将集成和适配器添加到他们的项目中。如果你希望你的适配器可以使用此工具安装,请将 astro-adapter 添加到 package.json 中的 keywords 字段:

¥The astro add command allows users to easily add integrations and adapters to their project. If you want your adapter to be installable with this tool, add astro-adapter to the keywords field in your package.json:

{
"name": "example",
"keywords": ["astro-adapter"],
}

完成 将你的适配器发布到 npm 后,运行 astro add example 将安装你的软件包以及 package.json 中指定的任何对等依赖。我们还将指导用户手动更新他们的项目配置。

¥Once you publish your adapter to npm, running astro add example will install your package with any peer dependencies specified in your package.json. We will also instruct users to update their project config manually.

¥Astro features

Added in: astro@3.0.0

Astro 功能是适配器告诉 Astro 是否能够支持某个功能以及适配器的支持级别的一种方式。

¥Astro features are a way for an adapter to tell Astro whether they are able to support a feature, and also the adapter’s level of support.

当使用这些属性时,Astro 将:

¥When using these properties, Astro will:

  • 运行特定验证;

  • 向日志发出上下文;

这些操作根据支持或不支持的功能、其支持级别以及用户使用的配置来运行。

¥These operations are run based on the features supported or not supported, their level of support, and the configuration that the user uses.

以下配置告诉 Astro 该适配器对资源具有实验性支持,但该适配器与内置服务 Sharp 和 Squoosh 不兼容:

¥The following configuration tells Astro that this adapter has experimental support for assets, but the adapter is not compatible with the built-in services Sharp and Squoosh:

my-adapter.mjs
export default function createIntegration() {
return {
name: '@matthewp/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@matthewp/my-adapter',
serverEntrypoint: '@matthewp/my-adapter/server.js',
supportedAstroFeatures: {
assets: {
supportKind: "experimental",
isSharpCompatible: false,
isSquooshCompatible: false
}
}
});
},
},
};
}

Astro 会将警告记录到终端:

¥Astro will log a warning to the terminal:

[@matthewp/my-adapter] The feature is experimental and subject to issues or changes.

如果用于资源的服务与适配器不兼容,则会出现错误:

¥and an error if the service used for assets is not compatible with the adapter:

[@matthewp/my-adapter] The currently selected adapter `@matthewp/my-adapter` is not compatible with the service "Sharp". Your project will NOT be able to build.

¥Adapter features

一组更改发出文件的输出的功能。当适配器选择使用这些功能时,他们将在特定的钩子中获得附加信息。

¥A set of features that changes the output of the emitted files. When an adapter opts in to these features, they will get additional information inside specific hooks.

这是仅在使用 SSR 时启用的功能。默认情况下,Astro 发出单个 entry.mjs 文件,该文件负责发出每个请求的渲染页面。

¥This is a feature that is enabled when using SSR only. By default, Astro emits a single entry.mjs file, which is responsible for emitting the rendered page on each request.

functionPerRoutetrue 时,Astro 将为项目中定义的每条路由创建一个单独的文件。

¥When functionPerRoute is true, Astro will instead create a separate file for each route defined in the project.

发出的每个文件只会渲染一页。这些页面将在 dist/pages/ 目录内(或在为 outDir 指定的目录中的 /pages/ 目录下)发出,并且发出的文件将保留与 src/pages/ 目录相同的文件路径。

¥Each file emitted will only render one page. The pages will be emitted inside a dist/pages/ directory (or under a /pages/ directory in the directory specified for outDir), and the emitted files will keep the same file paths of the src/pages/ directory.

构建的 pages/ 目录内的文件将镜像 src/pages/ 中页面文件的目录结构,例如:

¥The files inside the pages/ directory of the build will mirror the directory structure of your page files in src/pages/, for example:

  • Directorydist/
    • Directorypages/
      • Directoryblog/
        • entry._slug_.astro.mjs
        • entry.about.astro.mjs
      • entry.index.astro.mjs

通过将 true 传递给适配器来启用该功能。

¥Enable the feature by passing true to the adapter.

my-adapter.mjs
export default function createIntegration() {
return {
name: '@matthewp/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@matthewp/my-adapter',
serverEntrypoint: '@matthewp/my-adapter/server.js',
adapterFeatures: {
functionPerRoute: true
}
});
},
},
};
}

然后,使用钩子 astro:build:ssr,这将为你提供一个 entryPoints 对象,该对象将页面路由映射到构建后发出的物理文件。

¥Then, consume the hook astro:build:ssr, which will give you an entryPoints object that maps a page route to the physical file emitted after the build.

my-adapter.mjs
export default function createIntegration() {
return {
name: '@matthewp/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@matthewp/my-adapter',
serverEntrypoint: '@matthewp/my-adapter/server.js',
adapterFeatures: {
functionPerRoute: true
}
});
},
'astro:build:ssr': ({ entryPoints }) => {
for (const [route, entryFile] of entryPoints) {
// do something with route and entryFile
}
}
},
};
}

¥Serverless environments

在无服务器环境中设置 functionPerRoute: true 会为每个路由创建一个 JavaScript 文件(处理程序)。根据你的托管平台,处理程序可能有不同的名称:lambda、函数、页面等

¥Setting functionPerRoute: true in a serverless environment creates a JavaScript file (handler) for each route. A handler might have different names based on your hosting platform: lambda, function, page, etc.

当处理程序运行时,这些路由中的每一个都会受到 冷启动 的影响,这可能会导致一些延迟。这种延迟受到不同因素的影响。

¥Each of these routes is subject to a cold start when the handler runs, which may cause some delay. This delay is influenced by different factors.

对于 functionPerRoute: false,只有一个处理程序负责渲染所有路由。当首次触发此处理程序时,你将遭受冷启动。然后,所有其他路由应立即运行。但是,你将失去 functionPerRoute: true 提供的代码分割的好处。

¥With functionPerRoute: false, there is only one single handler in charge of rendering all your routes. When this handler is first triggered, you will be subject to a cold start. Then, all other routes should function without delay. However, you will lose the benefit of code splitting that functionPerRoute: true provides.

定义构建时是否打包任何 SSR 中间件代码。

¥Defines whether any SSR middleware code will be bundled when built.

启用后,这可以防止中间件代码在构建期间被所有页面打包和导入:

¥When enabled, this prevents middleware code from being bundled and imported by all pages during the build:

my-adapter.mjs
export default function createIntegration() {
return {
name: '@matthewp/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@matthewp/my-adapter',
serverEntrypoint: '@matthewp/my-adapter/server.js',
adapterFeatures: {
edgeMiddleware: true
}
});
},
},
};
}

然后,使用钩子 astro:build:ssr,这将为你提供文件系统上物理文件的 middlewareEntryPointURL

¥Then, consume the hook astro:build:ssr, which will give you a middlewareEntryPoint, an URL to the physical file on the file system.

my-adapter.mjs
export default function createIntegration() {
return {
name: '@matthewp/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@matthewp/my-adapter',
serverEntrypoint: '@matthewp/my-adapter/server.js',
adapterFeatures: {
edgeMiddleware: true
}
});
},
'astro:build:ssr': ({ middlewareEntryPoint }) => {
// remember to check if this property exits, it will be `undefined` if the adapter doesn't opt in to the feature
if (middlewareEntryPoint) {
createEdgeMiddleware(middlewareEntryPoint)
}
}
},
};
}
function createEdgeMiddleware(middlewareEntryPoint) {
// emit a new physical file using your bundler
}
Astro 中文网 - 粤ICP备13048890号