

这是 AWS CDK v2 开发者指南。旧版 CDK v1 于 2022 年 6 月 1 日进入维护阶段，并于 2023 年 6 月 1 日终止支持。

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

# 在 AWS CDK 中使用 JavaScript
<a name="work-with-cdk-javascript"></a>

JavaScript 是 AWS CDK 完全支持的客户端语言，被认为是稳定的。在中使用 C AWS loud Development Kit (AWS CDK) JavaScript 使用熟悉的工具，包括 [Node.js](https://nodejs.org/) 和 Node Package Manager (`npm`)。如果您愿意，也可以使用 [Yarn](https://yarnpkg.com/)，不过本指南中的示例使用的是 NPM。[构成 AWS 构造库的模块通过 NPM 存储库 npmjs.org 分发。](https://www.npmjs.com/)

您可以使用任何编辑器或 IDE。许多 AWS CDK 开发者使用 [Visual Studio Code（或其开源代码](https://code.visualstudio.com/)等效物 [VSCodium](https://vscodium.com/)），该代码有很好的支持。 JavaScript

## 开始使用 JavaScript
<a name="javascript-prerequisites"></a>

要使用 AWS CDK，你必须拥有 AWS 账户和凭证，并已安装 Node.js 和 AWS CDK Toolkit。请参阅 [AWS CDK 入门](getting-started.md)。

JavaScript AWS 除此之外，CDK 应用程序不需要其他先决条件。

**注意**  
第三方语言弃用：语言版本仅在供应商或社区共享其 EOL（生命周期终止）之前才受支持，如有更改，会另行通知。

## 创建项目
<a name="javascript-newproject"></a>

您可以通过在空目录`cdk init`中调用来创建新的 AWS CDK 项目。使用 `--language` 选项并指定 `javascript`：

```
$ mkdir my-project
$ cd my-project
$ cdk init app --language javascript
```

创建项目还会安装 [aws-cdk-lib](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib-readme.html) 模块及其依赖项。

 `cdk init` 使用项目文件夹的名称来命名项目的各种元素，包括类、子文件夹和文件。文件夹名称中的连字符都将转换为下划线。但是，除此之外，名称应遵循 JavaScript 标识符的形式；例如，名称不应以数字开头或包含空格。

## 使用本地 `cdk`
<a name="javascript-local"></a>

在大多数情况下，本指南假设您已全局安装 CDK Toolkit（`npm install -g aws-cdk`），并且提供的命令示例（例如 `cdk synth`）也遵循此假设。这种方法可以很容易让 CDK Toolkit 保持为最新版本，而且由于 CDK 采取了严格的向后兼容性方法，所以始终使用最新版本通常风险很小。

有些团队更喜欢在每个项目中指定所有依赖项，包括 CDK Toolkit 等工具。这种做法允许您将此类组件固定到特定版本，并确保您的团队（和您的 CI/CD 环境）中的所有开发人员都完全使用这些版本。这从根源上消除了可能发生的变更，有助于使构建和部署更加一致且可重复。

CDK 在 JavaScript 项目模板中包含对 CDK Toolkit 的依赖关系`package.json`，因此，如果您想使用这种方法，则无需对项目进行任何更改。您所需要做的就是使用稍微不同的命令来构建应用程序以及发出 `cdk` 命令。


| 操作 | 使用全局工具 | 使用本地工具 | 
| --- | --- | --- | 
|   **初始化项目**   |   `cdk init --language javascript`   |   `npx aws-cdk init --language javascript`   | 
|   **运行 CDK 工具包命令**   |   `cdk …​`   |   `npm run cdk …​` 或 `npx aws-cdk …​`   | 

 `npx aws-cdk` 运行当前项目中本地安装的 CDK Toolkit 版本（如果存在），如有则回退到全局安装。如果不存在全局安装，则 `npx` 会下载 CDK Toolkit 的临时副本并运行该副本。您可以使用 `@` 语法（`npx aws-cdk@1.120 --version` 打印 `1.120.0`）来指定 CDK Toolkit 的任意版本。

**提示**  
设置别名，以便您可以在安装本地 CDK Toolkit 时使用 `cdk` 命令。  

```
$ alias cdk="npx aws-cdk"
```

```
doskey cdk=npx aws-cdk $*
```

## 管理 AWS 构造库模块
<a name="javascript-managemodules"></a>

使用 Node Package Manager (`npm`) 安装和更新 C AWS onstruct Library 模块以供您的应用程序使用，以及您需要的其他软件包。（如果您愿意，可以用 `yarn` 代替 `npm`。）`npm` 还会自动安装这些模块的依赖项。

大多数 AWS CDK 构造都位于名为 CDK 的主包中`aws-cdk-lib`，这是由创建的新项目中的默认依赖项。`cdk init`“实验性” AWS 构造库模块（其中更高级别的构造仍在开发中）被命名为。`aws-cdk-lib/<SERVICE-NAME>-alpha`服务名称带有 *aws-* 前缀。如果您不确定某个模块的名称，请[在 NPM 上进行搜索](https://www.npmjs.com/search?q=%40aws-cdk)。

**注意**  
[CDK API Reference](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-construct-library.html) 也显示了包名称。

例如，以下命令安装的实验模块 AWS CodeStar。

```
npm install @aws-cdk/aws-codestar-alpha
```

某些服务的构造库支持位于多个命名空间中。例如，除 `aws-route53` 之外，还有另外三个 Amazon Route 53 命名空间，分别是 `aws-route53-targets`、`aws-route53-patterns` 和 `aws-route53resolver`。

您项目的依赖项在 `package.json` 中进行维护。您可以编辑此文件，将部分或全部依赖项锁定到特定版本，或者允许在特定条件下将其更新到较新版本。根据您在 `package.json` 中指定的规则，将项目的 NPM 依赖项更新到允许的最新版本：

```
npm update
```

在中 JavaScript，您可以将模块导入到代码中，其名称与使用 NPM 安装模块时使用的名称相同。在应用程序中导入 AWS CDK 类和 AWS 构造库模块时，我们建议采用以下做法。遵循这些准则将有助于使您的代码与其他 AWS CDK 应用程序保持一致，并且更易于理解。
+ 使用`require()`而不是 ES6样式`import`指令。Node.js 的旧版本不支持 ES6 导入，因此使用较旧的语法更具兼容性。（如果你真的想使用 ES6 导入，请使用 [esm](https://www.npmjs.com/package/esm) 来确保你的项目与所有支持的 Node.js 版本兼容。）
+ 通常，从 `aws-cdk-lib` 中导入单个类。

  ```
  const { App, Stack } = require('aws-cdk-lib');
  ```
+ 如果您需要 `aws-cdk-lib` 中的许多类，则可以使用 `cdk` 的命名空间别名，而不是导入单个类。避免同时执行这两项操作。

  ```
  const cdk = require('aws-cdk-lib');
  ```
+ 通常，使用短命名空间别名导入 AWS 构造库。

  ```
  const { s3 } = require('aws-cdk-lib/aws-s3');
  ```

## 在中管理依赖关系 JavaScript
<a name="work-with-cdk-javascript-dependencies"></a>

在 JavaScript CDK 项目中，依赖关系是在项目主目录`package.json`的文件中指定的。核心 AWS CDK 模块位于名`aws-cdk-lib`为的单个`NPM`包中。

当您使用 `npm install` 来安装包时，NPM 会在 `package.json` 中记录该包。

如果您愿意，可以用 Yarn 代替 NPM。但是，CDK 不支持 Yarn plug-and-play 模式，这是 Yarn 2 中的默认模式。将以下内容添加到项目的 `.yarnrc.yml` 文件以关闭此功能。

```
nodeLinker: node-modules
```

### CDK 应用程序
<a name="work-with-cdk-javascript-dependencies-apps"></a>

以下是 `cdk init --language typescript` 命令生成的示例 `package.json` 文件。为生成的文件 JavaScript 类似，只是没有 TypeScript相关的条目。

```
{
  "name": "my-package",
  "version": "0.1.0",
  "bin": {
    "my-package": "bin/my-package.js"
  },
  "scripts": {
    "build": "tsc",
    "watch": "tsc -w",
    "test": "jest",
    "cdk": "cdk"
  },
  "devDependencies": {
    "@types/jest": "^26.0.10",
    "@types/node": "10.17.27",
    "jest": "^26.4.2",
    "ts-jest": "^26.2.0",
    "aws-cdk": "2.16.0",
    "ts-node": "^9.0.0",
    "typescript": "~3.9.7"
  },
  "dependencies": {
    "aws-cdk-lib": "2.16.0",
    "constructs": "^10.0.0",
    "source-map-support": "^0.5.16"
  }
}
```

对于可部署的 CDK 应用程序，必须在 `package.json` 的 `dependencies` 部分中指定 `aws-cdk-lib`。您可以使用脱字符（^）版本号说明符来表示您将接受比指定版本更高的版本，前提是它们位于同一个主版本内。

对于实验构造，请为 alpha 构造库模块指定确切的版本，这些版本可能会 APIs 发生变化。请勿使用 ^ 或 \$1，因为这些模块的更高版本可能会导致 API 发生更改，从而导致您的应用程序中断。

在 `package.json` 的 `devDependencies` 部分中指定测试应用程序所需的库和工具版本（例如，`jest` 测试框架）。或者使用 ^ 来指定可接受更高版本的兼容版本。

### 第三方构造库
<a name="work-with-cdk-javascript-dependencies-libraries"></a>

如果您正在开发构造库，请结合 `peerDependencies` 和 `devDependencies` 部分来指定其依赖项，如以下示例 `package.json` 文件所示。

```
{
  "name": "my-package",
  "version": "0.0.1",
  "peerDependencies": {
    "aws-cdk-lib": "^2.14.0",
    "@aws-cdk/aws-appsync-alpha": "2.10.0-alpha",
    "constructs": "^10.0.0"
  },
  "devDependencies": {
    "aws-cdk-lib": "2.14.0",
    "@aws-cdk/aws-appsync-alpha": "2.10.0-alpha",
    "constructs": "10.0.0",
    "jsii": "^1.50.0",
    "aws-cdk": "^2.14.0"
  }
}
```

在 `peerDependencies` 中，使用脱字符（^）来指定您的库所使用的 `aws-cdk-lib` 的最低版本。这样可以最大限度地提高您的库与一系列 CDK 版本的兼容性。为 alpha 构造库模块指定确切的版本 APIs ，这些版本可能会发生变化。使用 `peerDependencies` 可以确保 `node_modules` 树中所有 CDK 库副本只有一个副本。

在 `devDependencies` 中，指定测试所需的工具和库，也可以使用 ^ 来表示可以接受更高版本的兼容版本。确切指定（不带 ^ 或 \$1）您宣称您的库与之兼容的 `aws-cdk-lib` 和其他 CDK 软件包的最低版本。这种做法可确保您的测试是针对这些版本运行的。这样，如果您无意中使用了仅较新版本中具备的功能，则您的测试可以将其捕捉到。

**警告**  
 只有 NPM 7 及更高版本才能自动安装 `peerDependencies`。如果您使用的是 NPM 6 或更早版本，或者使用的是 Yarn，则必须在 `devDependencies` 中包含依赖项的依赖项。否则，将不会进行安装，并且您将收到有关对等依赖项未解决的警告。

### 安装和更新依赖项
<a name="work-with-cdk-javascript-dependencies-install"></a>

运行以下命令来安装项目的依赖项。

**Example**  

```
# Install the latest version of everything that matches the ranges in 'package.json'
npm install

# Install the same exact dependency versions as recorded in 'package-lock.json'
npm ci
```

```
# Install the latest version of everything that matches the ranges in 'package.json'
yarn upgrade

# Install the same exact dependency versions as recorded in 'yarn.lock'
yarn install --frozen-lockfile
```

要更新已安装的模块，可以使用前面的 `npm install` 和 `yarn upgrade` 命令。任一命令都会将 `node_modules` 中的软件包更新为可满足 `package.json` 中规则的最新版本。但是，它们不会更新 `package.json` 本身，您可能需要执行此操作来设置新的最低版本。如果您在上托管软件包 GitHub，则可以将 De [pendabot 版本更新配置为自动更新](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuring-dependabot-version-updates)。`package.json`或者，请使用 [npm-check-updates](https://www.npmjs.com/package/npm-check-updates)。

**重要**  
根据设计，当您安装或更新依赖项时，NPM 和 Yarn 会选择可满足 `package.json` 中指定要求的每个包的最新版本。这些版本始终存在损坏风险（无论是无意还是有意）。更新项目的依赖项后，请彻底进行测试。

## AWS CDK 中的成语 JavaScript
<a name="javascript-cdk-idioms"></a>

### Props
<a name="javascript-props"></a>

所有 C AWS onstruct Library 类都使用三个参数进行实例化：定义构造的*作用域*（构造树中的父级）、*id* 和 *props*（构造函数用来配置其创建的 AWS 资源的一 key/value 组对）。其他类和方法也使用“属性捆绑包”模式作为参数。

使用具有良好 JavaScript 自动完成功能的 IDE 或编辑器将有助于避免拼写错误的属性名称。如果一个构造需要一个 `encryptionKeys` 属性，而您将其拼写为 `encryptionkeys`，那么在实例化构造时，您就没有传递您想要传递的值。如果该属性为必需，则这可能会导致在合成时出现错误；如果该属性为可选，则会无提示忽略该属性。在后一种情况下，您可能会得到一个您想要覆盖的默认行为。在这里要特别小心。

对 AWS 构造库类进行子类化（或重写采用类似 props 的参数的方法）时，您可能需要接受其他属性供自己使用。这些值将被父类或重写的方法忽略，因为在该代码中永远不会访问它们，因此通常可以传递收到的所有 props。

Future 版本的 AWS CDK 可能会巧合地添加一个新属性，其名称是你用于自己的财产。将收到的值沿继承链向上传递可能会导致意外行为。更安全的做法是传递您收到的 props 卷影副本，同时移除属性或将其设置为 `undefined`。例如：

```
super(scope, name, {...props, encryptionKeys: undefined});
```

或者，为您的属性命名，以便清楚地表明它们属于您的构造。这样，它们就不太可能在未来的 AWS CDK版本中与属性发生冲突。如果有很多属性，请使用一个适当命名的对象来存放它们。

### 缺失值
<a name="javascript-missing-values"></a>

对象（例如`props`）中的缺失值具有值`undefined` JavaScript。可以使用常用技术来处理这些问题。例如，以下是一个常见的习惯用法，用于访问可能未定义的值的属性：

```
// a may be undefined, but if it is not, it may have an attribute b
// c is undefined if a is undefined, OR if a doesn't have an attribute b
let c = a && a.b;
```

但是，如果 `a` 除 `undefined` 之外还可能具有其他“false”值，则最好让测试更加明确。在这里，我们将利用 `null` 等同于 `undefined` 的事实，来同时测试这两者：

```
let c = a == null ? a : a.b;
```

**提示**  
Node.js 14.0 及更高版本支持新的运算符，这些运算符可以简化未定义值的处理。有关更多信息，请参阅[可选链接](https://github.com/tc39/proposal-optional-chaining/blob/master/README.md)和 [Null 值合并](https://github.com/tc39/proposal-nullish-coalescing/blob/master/README.md)建议。

## 使用 TypeScript 示例 JavaScript
<a name="javascript-using-typescript-examples"></a>

 [TypeScript](https://www.typescriptlang.org/)是我们开发 AWS CDK 时使用的语言，也是开发应用程序时支持的第一种语言，因此编写了许多可用的 AWS CDK 代码示例。 TypeScript对于 JavaScript 开发人员来说，这些代码示例可能是一个很好的资源；你只需要删除代码中 TypeScript特定部分即可。

TypeScript 片段通常使用较新的 ECMAScript `import`和`export`关键字从其他模块导入对象，并声明要在当前模块之外提供的对象。Node.js 刚好在其最新版本中开始支持这些关键词。根据您正在使用（或希望支持）的 Node.js 版本，您可以重写导入和导出以使用旧语法。

可以将导入替换为对 `require()` 函数的调用。

**Example**  

```
import * as cdk from 'aws-cdk-lib';
import { Bucket, BucketPolicy } from 'aws-cdk-lib/aws-s3';
```

```
const cdk = require('aws-cdk-lib');
const { Bucket, BucketPolicy } = require('aws-cdk-lib/aws-s3');
```

可以将导出分配给 `module.exports` 对象。

**Example**  

```
export class Stack1 extends cdk.Stack {
  // ...
}

export class Stack2 extends cdk.Stack {
  // ...
}
```

```
class Stack1 extends cdk.Stack {
  // ...
}

class Stack2 extends cdk.Stack {
  // ...
}

module.exports = { Stack1, Stack2 }
```

**注意**  
使用旧式导入和导出的另一种方法是使用 [esm](https://www.npmjs.com/package/esm) 模块。

对导入和导出进行分类后，就可以深入研究实际的代码了。您可能会遇到以下常用功能 TypeScript ：
+ 类型注释
+ 接口定义
+ 类型转换/强制转换
+ 访问修饰符

可以为变量、类成员、函数参数和函数返回类型提供类型注释。对于变量、参数和成员，则通过在标识符后面加上冒号和类型来指定类型。函数返回值跟在函数签名后，由冒号和类型组成。

要将带类型注释的代码转换为 JavaScript，请删除冒号和类型。类成员中必须有一些值 JavaScript；`undefined`如果类成员中只有类型注释，则将其设置为 TypeScript。

**Example**  

```
var encrypted: boolean = true;

class myStack extends cdk.Stack {
    bucket: s3.Bucket;
    // ...
}

function makeEnv(account: string, region: string) : object {
    // ...
}
```

```
var encrypted = true;

class myStack extends cdk.Stack {
    bucket = undefined;
    // ...
}

function makeEnv(account, region) {
    // ...
}
```
在中 TypeScript，接口用于为必需属性和可选属性的捆绑包及其类型命名。然后，您可以使用接口名称作为类型注释。 TypeScript 将确保你用作函数参数的对象具有正确类型的必需属性。  

```
interface myFuncProps {
    code: lambda.Code,
    handler?: string
}
```

JavaScript 没有接口功能，因此删除类型注释后，请完全删除接口声明。

当函数或方法返回通用类型（例如`object`），但您想将该值视为更具体的子类型以访问不属于更一般类型接口的属性或方法时， TypeScript 允许您使用`as`后跟类型或接口名称来*转换*值。 JavaScript 不支持（或不需要）这个，所以只需删除`as`和以下标识符即可。一种不太常见的强制转换语法是在括号中使用类型名称 `<LikeThis>`；也必须删除这些强制转换。

最后， TypeScript 支持类成员的访问修饰符`public``protected`、和`private`。中的所有班级成员 JavaScript 都是公开的。在任何地方看到这些修饰符，将其删除即可。

知道如何识别和删除这些 TypeScript 功能对于使简短的 TypeScript 片段适应大有帮助. JavaScript 但是，以这种方式转换较长的 TypeScript 示例可能不切实际，因为它们更有可能使用其他 TypeScript 功能。对于这些情况，我们建议使用 [Sucrase](https://github.com/alangpierce/sucrase)。例如，如果代码使用了未定义的变量，Sucrase 就不会像 `tsc` 那样报错。如果它在语法上是有效的，那么除了少数例外，Sucrase 可以将其翻译为。 JavaScript因此，其对于转换可能无法自行运行的代码片段尤为有价值。

## 迁移到 TypeScript
<a name="javascript-to-typescript"></a>

[TypeScript](https://www.typescriptlang.org/)随着项目规模越来越大、越来越复杂，许多 JavaScript 开发人员开始使用。 TypeScript 是 JavaScript（所有 JavaScript 代码均为有效代码，因此无需对 TypeScript 代码进行任何更改）的超集，而且它也是一种受支持的 CDK 语言。 AWS 类型注释和其他 TypeScript 功能是可选的，当你发现其中的价值时，可以将其添加到你的 AWS CDK 应用程序中。 TypeScript 还允许您在新 JavaScript 功能最终确定之前抢先体验这些新功能，例如可选的链接和空值合并，而且无需升级 Node.js。

TypeScript的 “基于形状” 的接口，它定义了对象中的必需和可选属性（及其类型）的捆绑包，允许您在编写代码时发现常见错误，并使您的 IDE 更容易提供强大的自动完成功能和其他实时编码建议。

编程 TypeScript 确实涉及一个额外的步骤：使用编 TypeScript 译器编译应用程序`tsc`。对于典型的 AWS CDK 应用程序，编译最多需要几秒钟。

将现有 JavaScript AWS CDK 应用程序迁移到的最简单方法 TypeScript 是使用创建新 TypeScript 项目`cdk init app --language typescript`，然后将您的源文件（以及任何其他必需的文件，例如 Lamb AWS da 函数源代码等资产）复制到新项目。将 JavaScript 文件重命名为结尾`.ts`并开始开发 TypeScript。