

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 使用 Amplify Hosting 部署规范配置构建输出
<a name="ssr-deployment-specification"></a>

Amplify Hosting 部署规范是基于文件系统的规范，它定义了便于部署到 Amplify Hosting 的目录结构。框架可以生成这种预期的目录结构作为其构建命令的输出，使框架能够利用 Amplify Hosting 的服务基元。Amplify Hosting 了解部署包的结构并相应地对其进行部署。

有关解释如何使用部署规范的视频演示，请参阅*如何在 Amazon Web Services YouTube 频道 AWS Amplify上使用托管任何网站*。




以下是 Amplify 期望部署包采用的文件夹结构示例。简而言之，它有一个名为 `static` 的文件夹、一个名为 `compute` 的文件夹和一个名为 `deploy-manifest.json` 的部署清单文件。

```
.amplify-hosting/
├── compute/
│   └── default/
│       ├── chunks/
│       │   └── app/
│       │       ├── _nuxt/
│       │       │   ├── index-xxx.mjs
│       │       │   └── index-styles.xxx.js
│       │       └── server.mjs
│       ├── node_modules/
│       └── server.js
├── static/
│   ├── css/
│   │   └── nuxt-google-fonts.css
│   ├── fonts/
│   │   ├── font.woff2
│   ├── _nuxt/
│   │   ├── builds/
│   │   │   └── latest.json
│   │   └── entry.xxx.js
│   ├── favicon.ico
│   └── robots.txt
└── deploy-manifest.json
```

## Amplify SSR 基元支持
<a name="ssr-primitive-support"></a>

 Amplify Hosting 部署规范定义了与以下基元紧密映射的合约。

**静态资产**  
为框架提供托管静态文件的功能。

**计算**  
使框架能够在端口 3000 上运行 Node.js HTTP 服务器。

**图像优化**  
为框架提供在运行时优化图像的服务。

**路由规则**  
为框架提供一种将传入请求路径映射到特定目标的机制。

## .amplify-hosting/static 目录
<a name="static-directory"></a>

必须将本应通过应用程序 URL 提供的所有可公开访问的静态文件放在 `.amplify-hosting/static` 目录中。此目录中的文件通过静态资产基元提供。

可以在应用程序 URL 的根（/）处访问静态文件，而无需对其内容、文件名或扩展名进行任何更改。此外，子目录保留在 URL 结构中，并出现在文件名之前。例如，`.amplify-hosting/static/favicon.ico` 将从 `https://myAppId.amplify-hostingapp.com/favicon.ico` 中提供，`.amplify-hosting/static/_nuxt/main.js` 将从 ` https://myAppId.amplify-hostingapp.com/_nuxt/main.js` 中提供

如果框架支持修改应用程序基本路径的功能，则它必须在 `.amplify-hosting/static` 目录内的静态资产前面加上基本路径。例如，如果基本路径是 `/folder1/folder2`，则名为 `main.css` 的静态资源的构建输出将是 `.amplify-hosting/static/folder1/folder2/main.css`。

## .amplify-hosting/compute 目录
<a name="compute-directory"></a>

单个计算资源由 `default` 目录中包含的名为 `.amplify-hosting/compute` 的单个子目录表示。路径是 `.amplify-hosting/compute/default`。此计算资源映射到 Amplify Hosting 的计算基元。

子目录 `default` 的内容必须符合以下规则。
+ 文件必须存在于子目录 `default` 的根目录中，才能作为计算资源的入口点。
+ 入口点文件必须是 Node.js 模块，并且它必须启动一个在端口 3000 上侦听的 HTTP 服务器。
+ 您可以将其他文件放在 `default` 子目录中，并从入口点文件中的代码中引用它们。
+ 子目录的内容必须是自包含的。入口点模块中的代码不能引用子目录之外的任何模块。请注意，框架可以以任何方式捆绑其 HTTP 服务器。如果可以在子目录中使用 `node server.js` 命令（其中 `server.js is` 是入口文件的名称）启动计算过程，则 Amplify 认为目录结构符合部署规范。

Amplify Hosting 将 `default` 子目录中的所有文件捆绑并部署到预置的计算资源中。每个计算资源被分配 512 MB 的临时存储。此存储不在执行实例之间共享，而是在同一执行实例中的后续调用之间共享。执行实例的最大执行时间限制为 15 分钟，并且执行实例中唯一可写的路径是 `/tmp` 目录。每个计算资源捆绑包的未压缩大小不能超过 220 MB。例如，未压缩的 `.amplify/compute/default` 子目录不能超过 220 MB。

## .amplify-hosting/deploy-manifest.json 文件
<a name="deployment-manifest-json"></a>

使用 `deploy-manifest.json` 文件存储部署的配置详细信息和元数据。`deploy-manifest.json` 文件必须至少包含一个 `version` 属性、指定了捕获所有路由的 `routes` 属性以及以及指定了框架元数据的 `framework` 属性。

以下对象定义演示了部署清单的配置。

```
type DeployManifest = {
  version: 1;
  routes: Route[];
  computeResources?: ComputeResource[];
  imageSettings?: ImageSettings;
  framework: FrameworkMetadata;
};
```

以下主题描述了部署清单中每个属性的详细信息和用法。

### 使用版本属性
<a name="deployment-manifest-version"></a>

`version` 属性定义了您正在实施的部署规范的版本。目前，Amplify Hosting 部署规范的唯一版本是版本 1。以下 JSON 示例说明了如何使用 `version` 属性。

```
"version": 1
```

### 使用路由属性
<a name="deployment-manifest-routes"></a>

`routes` 属性使框架能够利用 Amplify Hosting 路由规则基元。路由规则提供了一种机制，用于将传入的请求路径路由到部署包中的特定目标。路由规则仅规定传入请求的目的地，并在通过重写和重定向规则转换请求后应用。有关 Amplify Hosting 如何处理重写和重定向的更多信息，请参阅[为 Amplify 应用程序设置重定向和重写](redirects.md)

路由规则不会重写或转换请求。如果传入的请求与路由的路径模式匹配，则该请求将按原样路由到路径的目标。

`routes` 数组中指定的路由规则必须符合以下规则。
+ 必须指定捕获所有路由。捕获所有路由具有匹配所有传入请求的 `/*` 模式。
+ `routes` 数组最多可以包含 25 个项目。
+ 您必须指定 `Static` 路由或 `Compute` 路由。
+ 如果指定 `Static` 路由，则 `.amplify-hosting/static` 目录必须存在。
+ 如果指定 `Compute` 路由，则 `.amplify-hosting/compute` 目录必须存在。
+ 如果指定 `ImageOptimization` 路由，则还必须指定 `Compute` 路由。这是必要操作，因为纯静态应用程序尚不支持图像优化。

以下对象定义演示了 `Route` 对象的配置。

```
type Route = {
  path: string;
  target: Target;
  fallback?: Target;
}
```

以下列表描述了 `Route` 对象的属性。


| Key | Type | 必需 | 说明 | 
| --- | --- | --- | --- | 
|  path  |  字符串  |  是  |  定义与传入请求路径（不包括查询字符串）相匹配的模式。 路径最大长度为 255 个字符。 路径必须以正斜杠 `/` 开头。 路径可以包含以下任何字符：[A-Z]、[a-z]、[0-9]、[ \$1-.\$1\$1/\$1"'@:\$1]。 对于模式匹配，仅支持以下通配符： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/amplify/latest/userguide/ssr-deployment-specification.html)  | 
|  target  |  Target  |  是  |  一个对象，用于定义将匹配的请求路由到的目标。 如果指定了 `Compute` 路由，则必须存在相应的 `ComputeResource`。 如果指定了 `ImageOptimization`，则还必须指定 `imageSettings`。  | 
|  回退  |  Target  |  否  |  一个对象，用于定义原始目标返回 404 错误时要回退到的目标。 指定路径的 `fallback` 种类和 `target` 种类不能相同。例如，不允许从 `Static` 回退到 `Static`。只有没有正文的 GET 请求才支持回退。如果请求中存在正文，则将在回退期间将其丢弃。  | 

以下对象定义演示了 `Target` 对象的配置。

```
type Target = {
  kind: TargetKind;
  src?: string;
  cacheControl?: string;
}
```

以下列表描述了 `Target` 对象的属性。


| Key | Type | 必需 | 说明 | 
| --- | --- | --- | --- | 
|  类型  |  Targetkind  |  是  |  定义目标类型的 `enum`。有效值包括 `Static`、`Compute` 和 `ImageOptimization`。  | 
|  src  |  字符串  |  对于 `Compute`，为是  对于其他基元，为否  |  一个字符串，用于指定包含基元的可执行代码的部署包中的子目录的名称。仅对计算基元有效，且为必填项。 该值必须指向部署包中存在的计算资源之一。目前，此字段唯一支持的值为 `default`。  | 
|  cacheControl  |  字符串  |  否  |  一个字符串，它指定要应用于响应的 Cache-Control 标头的值。仅对静态和 ImageOptimization基元有效。 指定的值会被自定义标头覆盖。有关 Amplify Hosting 客户标头的更多信息，请参阅[为 Amplify 应用程序设置自定义 HTTP 标头](custom-headers.md)。  此 Cache-Control 标头仅适用于状态码设置为 200（OK）的成功响应。   | 

以下对象定义演示了 `TargetKind` 枚举的用法。

```
enum TargetKind {
  Static = "Static",
  Compute = "Compute",
  ImageOptimization = "ImageOptimization"
}
```

以下列表指定了 `TargetKind` 枚举的有效值。

**静态**  
将请求路由到静态资产基元。

**计算**  
将请求路由到计算基元。

**ImageOptimization**  
将请求路由到图像优化基元。

以下 JSON 示例说明了如何使用指定了多个路由规则的 `routes` 属性。

```
"routes": [
    {
      "path": "/_nuxt/image",
      "target": {
        "kind": "ImageOptimization",
        "cacheControl": "public, max-age=3600, immutable"
      }
    },
    {
      "path": "/_nuxt/builds/meta/*",
      "target": {
        "cacheControl": "public, max-age=31536000, immutable",
        "kind": "Static"
      }
    },
    {
      "path": "/_nuxt/builds/*",
      "target": {
        "cacheControl": "public, max-age=1, immutable",
        "kind": "Static"
      }
    },
    {
      "path": "/_nuxt/*",
      "target": {
        "cacheControl": "public, max-age=31536000, immutable",
        "kind": "Static"
      }
    },
    {
      "path": "/*.*",
      "target": {
        "kind": "Static"
      },
      "fallback": {
        "kind": "Compute",
        "src": "default"
      }
    },
    {
      "path": "/*",
      "target": {
        "kind": "Compute",
        "src": "default"
      }
    }
  ]
```

有关在部署清单中指定路由规则的详细信息，请参阅[配置路由规则的最佳实践](#routing-best-practices)。

### 使用 computeResources 属性
<a name="deployment-manifest-computeResources"></a>

`computeResources` 属性使框架能够提供有关预置计算资源的元数据。每个计算资源都必须有与之关联的相应路由。

以下对象定义演示了 `ComputeResource` 对象的用法。

```
type ComputeResource = {
  name: string;
  runtime: ComputeRuntime;
  entrypoint: string;
};
 
type ComputeRuntime = 'nodejs20.x' | 'nodejs22.x';
```

以下列表描述了 `ComputeResource` 对象的属性。


| Key | Type | 必需 | 说明 | 
| --- | --- | --- | --- | 
|  name  |  字符串  |  是  |  指定计算资源的名称。名称必须与 `.amplify-hosting/compute directory` 内的子目录名称匹配。 对于部署规范的版本 1，唯一的有效值为 `default`。  | 
|  运行时  |  ComputeRuntime  |  是  |  定义预置计算资源的运行时。 有效值为 `nodejs20.x` 和 `nodejs22.x`。  | 
|  entrypoint  |  字符串  |  是  |  为指定的计算资源指定代码将从中运行的启动文件的名称。该文件必须存在于代表计算资源的子目录中。  | 

您的目录结构必须与以下结构类似。

```
.amplify-hosting
|---compute
|   |---default
|       |---index.js
```

`computeResource` 属性的 JSON 如下所示。

```
"computeResources": [
    {
      "name": "default",
      "runtime": "nodejs20.x",
      "entrypoint": "index.js",
    }
  ]
```

### 使用 imageSettings 属性
<a name="deployment-manifest-imageSettings"></a>

`imageSettings` 属性使框架能够自定义图像优化基元的行为，该基元在运行时提供图像的按需优化。

以下对象定义演示了 `ImageSettings` 对象的用法。

```
type ImageSettings = {
  sizes: number[];
  domains: string[];
  remotePatterns: RemotePattern[];
  formats: ImageFormat[];
  minumumCacheTTL: number;
  dangerouslyAllowSVG: boolean;
};
 
type ImageFormat = 'image/avif' | 'image/webp' | 'image/png' | 'image/jpeg';
```

以下列表描述了 `ImageSettings` 对象的属性。


| Key | Type | 必需 | 说明 | 
| --- | --- | --- | --- | 
|  尺寸  |  Number[]  |  是  |  支持的图像宽度数组。  | 
|  域  |  String[]  |  是  |  允许使用图像优化的外部域的数组。将数组留空，仅允许部署域使用图像优化。  | 
|  remotePatterns  |  RemotePattern[]  |  是  |  允许使用图像优化的外部模式的数组。与域类似，但通过正则表达式（regex）提供了更多控制。  | 
|  格式  |  ImageFormat[]  |  是  |  允许的输出图像格式的数组。  | 
|  minimumCacheTTL  |  数字  |  是  |  优化图像的缓存时长（以秒为单位）。  | 
|  dangerouslyAllowSVG  |  布尔值  |  是  |  允许 SVG 输入图像 URLs。默认情况下，出于安全考虑，此功能处于禁用状态。  | 

以下对象定义演示了 `RemotePattern` 对象的用法。

```
type RemotePattern = {
  protocol?: 'https';
  hostname: string;
  port?: string;
  pathname?: string;
}
```

以下列表描述了 `RemotePattern` 对象的属性。


| Key | Type | 必需 | 说明 | 
| --- | --- | --- | --- | 
|  protocol  |  字符串  |  否  |  允许的远程模式的协议。唯一有效值为 `https`。  | 
|  hostname  |  字符串  |  是  |  允许的远程模式的主机名。 您可以指定文本或通配符。单个 `\$1` 匹配单个子域。双 `\$1\$1` 匹配任意数量的子域。在仅指定 `\$1\$1` 的情况下，Amplify 不允许使用笼统通配符。  | 
|  端口  |  字符串  |  否  |  允许的远程模式的端口。  | 
|  pathname  |  字符串  |  否  |  允许的远程模式的路径名称。  | 

以下示例说明了 `imageSettings` 属性。

```
"imageSettings": { 
    "sizes": [
      100,
      200
    ],
    "domains": [
      "example.com"
    ],
    "remotePatterns": [
      {
        "protocol": "https",
        "hostname": "example.com",
        "port": "",
        "pathname": "/**",
      }
    ],
    "formats": [
      "image/webp"
    ],
    "minumumCacheTTL": 60,
    "dangerouslyAllowSVG": false
  }
```

### 使用框架属性
<a name="deployment-manifest-framework"></a>

使用 `framework` 属性来指定框架元数据。

以下对象定义演示了 `FrameworkMetadata` 对象的配置。

```
type FrameworkMetadata = {
  name: string;
  version: string;
}
```

以下列表描述了 `FrameworkMetadata` 对象的属性。


| Key | Type | 必需 | 说明 | 
| --- | --- | --- | --- | 
|  name  |  字符串  |  是  |  框架的名称。  | 
|  版本  |  字符串  |  是  |  框架的版本。 它必须是有效的语义版本控制（semver）字符串。  | 

## 配置路由规则的最佳实践
<a name="routing-best-practices"></a>

路由规则提供了一种机制，用于将传入的请求路径路由到部署包中的特定目标。在部署捆绑包中，框架作者可以将部署到以下任一目标的文件发送到构建输出：
+ **静态资源基元** – 文件包含在 `.amplify-hosting/static` 目录中。
+ **计算基元** – 文件包含在 `.amplify-hosting/compute/default` 目录中。

框架作者还在部署清单文件中提供了一系列路由规则。数组中的每条规则都按顺序与传入的请求进行匹配，直到完成匹配为止。当存在匹配规则时，请求会被路由到匹配规则中指定的目标。或者，可以为每条规则指定一个回退目标。如果原始目标返回 404 错误，则会将请求路由到回退目标。

部署规范*要求*遍历顺序中的最后一条规则是捕获所有规则。使用 `/*` 路径指定了捕获所有规则。如果传入的请求与路由规则数组中先前的任何路由都不匹配，则该请求将被路由到捕获所有规则目标。

对于像 Nuxt.js 这样的 SSR 框架，捕获所有规则目标必须是计算基元。这是因为 SSR 应用程序具有服务器端渲染的页面，这些页面的路由在构建时是不可预测的。例如，如果 Nuxt.js 应用程序在 `[slug]` 处有一个页面，则其中 `/blog/[slug]` 是动态路由参数。捕获所有规则目标是将请求路由到这些页面的唯一方法。

相比之下，可以使用特定的路径模式来定位构建时已知的路由。例如，Nuxt.js 从 `/_nuxt` 路径中提供静态资产。这意味着 `/_nuxt/*` 路径可以由特定的路由规则来定向，该规则将请求路由到静态资产基元。

### 公共文件夹路由
<a name="public-folder-routing"></a>

大多数 SSR 框架提供了从 `public` 文件夹提供可变静态资产的能力。`favicon.ico` 和 `robots.txt` 之类的文件通常保存在 `public` 文件夹中，并通过应用程序的根 URL 提供。例如，`favicon.ico` 文件是从 `https://example.com/favicon.ico` 提供的。请注意，这些文件没有可预测的路径模式。它们几乎完全由文件名决定。在 `public` 文件夹中定位文件的唯一方法是使用捕获所有路由。但是，捕获所有路由目标必须是计算基元。

我们建议使用以下方法之一来管理 `public` 文件夹。

1. 使用路径模式来定位包含文件扩展名的请求路径。例如，您可以使用 ` /*.*` 定位所有包含文件扩展名的请求路径。

   请注意，这种方法可能不可靠。例如，如果 `public` 文件夹内有没有文件扩展名的文件，则此规则不针对这些文件。使用这种方法需要注意的另一个问题是，应用程序的页面名称中可能有句点。例如，`/blog/2021/01/01/hello.world` 处的页面将被 `/*.* ` 规则将定位。这并不理想，因为该页面不是静态资产。但是，您可以在此规则中添加回退目标，以确保当静态基元出现 404 错误时，请求会回退到计算基元。

   ```
   {
       "path": "/*.*",
       "target": {
           "kind": "Static"
       },
       "fallback": {
           "kind": "Compute",
           "src": "default"
       }
   }
   ```

1. 在构建时识别 `public` 文件夹中的文件，并为每个文件发出路由规则。这种方法不可扩展，因为部署规范规定了 25 条规则的限制。

   ```
   {
       "path": "/favicon.ico",
       "target": {
           "kind": "Static"
       }
   },
   {
       "path": "/robots.txt",
       "target": {
           "kind": "Static"
       }
   }
   ```

1. 建议您的框架用户将所有可变的静态资源存储在 `public` 文件夹内的子文件夹中。

   在以下示例中，用户可以将所有可变的静态资产存储在 `public/assets` 文件夹中。然后，可以使用带有路径模式 `/assets/*` 的路由规则来定位 `public/assets` 文件夹内所有可变的静态资产。

   ```
   {
       "path": "/assets/*",
       "target": {
           "kind": "Static"
       }
   }
   ```

1. 为捕获所有路由指定一个静态回退。这种方法的缺点将在下一节“[捕获所有回退路由](#catchall-fallback-routing)”中详细介绍。

### 捕获所有回退路由
<a name="catchall-fallback-routing"></a>

对于 Nuxt.js 等 SSR 框架，例如为计算基元目标指定了捕获所有路由，框架作者可以考虑为捕获所有路由指定静态回退以解决 `public` 文件夹路由问题。但是，这种类型的路由规则会破坏服务器端渲染的 404 页面。例如，如果最终用户访问了一个不存在的页面，则应用程序会呈现一个状态码为 404 的 404 页面。但是，如果捕获所有路由具有静态回退，则不会呈现 404 页面。取而代之的是，请求会回退到静态基元中，但最终仍会显示 404 状态码，但是 404 页面无法渲染。

```
{
    "path": "/*",
    "target": {
        "kind": "Compute",
        "src": "default"
    },
    "fallback": {
        "kind": "Static"
    }
}
```

#### 基本路径路由
<a name="base-path-routing"></a>

提供修改应用程序基本路径功能的框架应预先设置 `.amplify-hosting/static` 目录内静态资产的基本路径。例如，如果基本路径是 `/folder1/folder2`，则名为 main.css 的静态资源的构建输出将是 `.amplify-hosting/static/folder1/folder2/main.css`。

这意味着还需要更新路由规则以反映基本路径。例如，如果基本路径是 `/folder1/folder2`，则 `public` 文件夹中静态资产的路由规则将如下所示。

```
{
    "path": "/folder1/folder2/*.*",
    "target": {
        "kind": "Static"
    }
}
```

同样，服务器端路由也需要在它们前面加上基本路径。例如，如果基本路径是 `/folder1/folder2`，则 `/api` 路由的路由规则将如下所示。

```
{
    "path": "/folder1/folder2/api/*",
    "target": {
        "kind": "Compute",
        "src": "default"
    }
}
```

但是，不应将基本路径置于捕获所有路由之前。例如，如果基本路径是 `/folder1/folder2`，则捕获所有路由将保持如下所示。

```
{
    "path": "/*",
    "target": {
        "kind": "Compute",
        "src": "default"
    }
}
```

#### Nuxt.js 路由示例
<a name="Nuxtjs-routes-example"></a>

以下是 Nuxt 应用程序的示例 `deploy-manifest.json` 文件，演示了如何指定路由规则。

```
{
  "version": 1,
  "routes": [
    {
      "path": "/_nuxt/image",
      "target": {
        "kind": "ImageOptimization",
        "cacheControl": "public, max-age=3600, immutable"
      }
    },
    {
      "path": "/_nuxt/builds/meta/*",
      "target": {
        "cacheControl": "public, max-age=31536000, immutable",
        "kind": "Static"
      }
    },
    {
      "path": "/_nuxt/builds/*",
      "target": {
        "cacheControl": "public, max-age=1, immutable",
        "kind": "Static"
      }
    },
    {
      "path": "/_nuxt/*",
      "target": {
        "cacheControl": "public, max-age=31536000, immutable",
        "kind": "Static"
      }
    },
    {
      "path": "/*.*",
      "target": {
        "kind": "Static"
      },
      "fallback": {
        "kind": "Compute",
        "src": "default"
      }
    },
    {
      "path": "/*",
      "target": {
        "kind": "Compute",
        "src": "default"
      }
    }
  ],
  "computeResources": [
    {
      "name": "default",
      "entrypoint": "server.js",
      "runtime": "nodejs22.x"
    }
  ],
  "framework": {
    "name": "nuxt",
    "version": "3.8.1"
  }
}
```

以下是 Nuxt 的示例 `deploy-manifest.json` 文件，演示了如何指定包括基本路径在内的路由规则。

```
{
  "version": 1,
  "routes": [
    {
      "path": "/base-path/_nuxt/image",
      "target": {
        "kind": "ImageOptimization",
        "cacheControl": "public, max-age=3600, immutable"
      }
    },
    {
      "path": "/base-path/_nuxt/builds/meta/*",
      "target": {
        "cacheControl": "public, max-age=31536000, immutable",
        "kind": "Static"
      }
    },
    {
      "path": "/base-path/_nuxt/builds/*",
      "target": {
        "cacheControl": "public, max-age=1, immutable",
        "kind": "Static"
      }
    },
    {
      "path": "/base-path/_nuxt/*",
      "target": {
        "cacheControl": "public, max-age=31536000, immutable",
        "kind": "Static"
      }
    },
    {
      "path": "/base-path/*.*",
      "target": {
        "kind": "Static"
      },
      "fallback": {
        "kind": "Compute",
        "src": "default"
      }
    },
    {
      "path": "/*",
      "target": {
        "kind": "Compute",
        "src": "default"
      }
    }
  ],
  "computeResources": [
    {
      "name": "default",
      "entrypoint": "server.js",
      "runtime": "nodejs22.x"
    }
  ],
  "framework": {
    "name": "nuxt",
    "version": "3.8.1"
  }
}
```

有关使用 `routes` 属性的更多信息，请参阅[使用路由属性](#deployment-manifest-routes)。