这是 AWS CDK v2 开发者指南。较旧的 CDK v1 于 2022 年 6 月 1 日进入维护阶段,并于 2023 年 6 月 1 日终止支持。
本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
在 JavaScript 中使用 AWS CDK
JavaScript 是一种 AWS CDK 完全支持且视为稳定的客户端语言。在 JavaScript 中使用 AWS Cloud Development Kit (AWS CDK) 采用了熟悉的工具,包括 Node.jsnpm
)。如果您愿意,也可以使用 Yarn
您可以使用任何编辑器或 IDE。许多 AWS CDK 开发人员都使用 Visual Studio 代码
主题
开始使用 JavaScript
要使用 AWS CDK,您必须拥有 AWS 账户和凭证,并已安装 Node.js 和 AWS CDK Toolkit。请参阅 开始使用 AWS CDK。
除此之外,JavaScript AWS CDK 应用程序不需要其他先决条件。
注意
第三方语言弃用:语言版本仅在供应商或社区共享其 EOL(生命周期终止)之前才受支持,如有更改,会另行通知。
创建项目
您可以通过在空目录中调用 cdk init
来创建一个新的 AWS CDK 项目。使用 --language
选项并指定 javascript
:
mkdir my-project cd my-project cdk init app --language javascript
创建项目还会安装 aws-cdk-lib
模块及其依赖项。
cdk init
使用项目文件夹的名称来命名项目的各种元素,包括类、子文件夹和文件。文件夹名称中的连字符都将转换为下划线。但是,名称应遵循 JavaScript 标识符的形式;例如,名称不应以数字开头,也不应包含空格。
使用本地 cdk
在大多数情况下,本指南假设您已全局安装 CDK Toolkit (npm install -g aws-cdk
),并且提供的命令示例(例如 cdk synth
)也遵循此假设。这种方法可以很容易让 CDK Toolkit 保持为最新版本,而且由于 CDK 采取了严格的向后兼容性方法,所以始终使用最新版本通常风险很小。
有些团队更喜欢在每个项目中指定所有依赖项,包括 CDK Toolkit 等工具。通过这种做法,您可以将此类组件固定到特定版本,并确保团队中的所有开发人员(以及您的 CI/CD 环境)都恰好使用这些版本。这从根源上消除了可能发生的变更,有助于使构建和部署更加一致且可重复。
CDK 在 JavaScript 项目模板的 package.json
中包含了 CDK Toolkit 的依赖项,因此,如果您想使用这种方法,无需对项目进行任何更改。您所需要做的就是使用稍微不同的命令来构建应用程序以及发出 cdk
命令。
操作 | 使用全局 CDK Toolkit | 使用本地 CDK Toolkit |
---|---|---|
初始化项目 | cdk init --language javascript |
npx aws-cdk init --language javascript |
运行 CDK Toolkit 命令 | cdk ... |
npm run cdk ... or 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
命令。
管理 AWS 构造库模块
使用节点软件包管理器 (npm
) 来安装和更新供您应用程序使用的 AWS 构造库模块以及您需要的其他软件包。(如果您愿意,可以用 yarn
代替 npm
。)npm
还会自动安装这些模块的依赖项。
大多数 AWS CDK 构造都位于名为 aws-cdk-lib
的 CDK 主包中,这是 cdk init 创建的新项目中的默认依赖项。“实验性”AWS 构造库模块(其中更高级别的构造仍在开发中)的命名类似于 aws-cdk-lib/
。服务名称带有 aws- 前缀。如果您不确定某个模块的名称,请在 NPM 上进行搜索SERVICE-NAME
-alpha
注意
CDK API Reference 也显示了包名称。
例如,以下命令用于安装 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来确保您的项目与所有支持的 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 中管理依赖项
在 JavaScript CDK 项目中,在项目主目录的 package.json
文件中指定依赖项。核心 AWS CDK 模块位于名为 aws-cdk-lib
的单个 NPM 包中。
当您使用 npm install 来安装包时,NPM 会在 package.json
中记录该包。
如果您愿意,可以用 Yarn 代替 NPM。但是,CDK 不支持 Yarn 的即插即用模式,该模式是 Yarn 2 中的默认模式。将以下内容添加到项目的 .yarnrc.yml
文件以关闭此功能。
nodeLinker: node-modules
CDK 应用程序
以下是 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 构造库模块指定确切版本,这些模块的 API 可能会发生更改。请勿使用 ^ 或 ~,因为这些模块的更高版本可能会导致 API 发生更改,从而导致您的应用程序中断。
在 package.json
的 devDependencies
部分中指定测试应用程序所需的库和工具版本(例如,jest
测试框架)。或者使用 ^ 来指定可接受更高版本的兼容版本。
第三方构造库
如果您正在开发构造库,请结合 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 构造库模块指定确切版本,这些模块的 API 可能会发生更改。使用 peerDependencies
可以确保 node_modules
树中所有 CDK 库副本只有一个副本。
在 devDependencies
中,指定测试所需的工具和库,也可以使用 ^ 来表示可以接受更高版本的兼容版本。确切指定(不带 ^ 或 ~)您宣称您的库与之兼容的 aws-cdk-lib
和其他 CDK 软件包的最低版本。这种做法可确保您的测试是针对这些版本运行的。这样,如果您无意中使用了仅较新版本中具备的功能,则您的测试可以将其捕捉到。
警告
只有 NPM 7 及更高版本才能自动安装 peerDependencies
。如果您使用的是 NPM 6 或更早版本,或者使用的是 Yarn,则必须在 devDependencies
中包含依赖项的依赖项。否则,将不会进行安装,并且您将收到有关对等依赖项未解决的警告。
安装和更新依赖项
使用以下命令来安装项目的依赖项。
要更新已安装的模块,可以使用前面的 npm install 和 yarn upgrade 命令。任一命令都会将 node_modules
中的软件包更新为可满足 package.json
中规则的最新版本。但是,它们不会更新 package.json
本身,您可能需要执行此操作来设置新的最低版本。如果您将包托管在 GitHub 上,则可以配置 Dependabot 版本更新package.json
。或者,可以使用 npm-check-updates
重要
根据设计,当您安装或更新依赖项时,NPM 和 Yarn 会选择可满足 package.json
中指定要求的每个包的最新版本。这些版本始终存在损坏风险(无论是无意还是有意)。更新项目的依赖项后,请彻底进行测试。
JavaScript 中的 AWS CDK 习语
Props
所有 AWS 构造库类都使用三个参数进行实例化:在其中定义构造的 scope(构造树中的父级)、id 和 props(构造用于配置其创建的 AWS 资源的键/值对捆绑包)。其他类和方法也使用“属性捆绑包”模式作为参数。
使用具有良好 JavaScript 自动完成功能的 IDE 或编辑器将有助于避免属性名称出现拼写错误。如果一个构造需要一个 encryptionKeys
属性,而您将其拼写为 encryptionkeys
,那么在实例化构造时,您就没有传递您想要传递的值。如果该属性为必需,则这可能会导致在合成时出现错误;如果该属性为可选,则会无提示忽略该属性。在后一种情况下,您可能会得到一个您想要覆盖的默认行为。在这里要特别小心。
对 AWS 构造库类进行子类化(或重写采用类似 props 参数的方法)时,您可能需要接受其他属性供自己使用。这些值将被父类或重写的方法忽略,因为在该代码中永远不会访问它们,因此通常可以传递收到的所有 props。
AWS CDK 的未来版本可能会碰巧添加一个新属性,其名称与您用于自己属性的名称相同。将收到的值沿继承链向上传递可能会导致意外行为。更安全的做法是传递您收到的 props 的浅副本,同时删除属性或将其设置为 undefined
。例如:
super(scope, name, {...props, encryptionKeys: undefined});
或者,为您的属性命名,以便清楚地表明它们属于您的构造。这样,其就不太可能在未来的 AWS CDK 版本中与属性发生冲突。如果有很多属性,请使用一个适当命名的对象来存放它们。
缺失值
对象中的缺失值(例如 props
)在 JavaScript 中的值为 undefined
。可以使用常用技术来处理这些问题。例如,以下是一个常见的习惯用法,用于访问可能未定义的值的属性:
// 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;
将 TypeScript 示例与 JavaScript 结合使用
TypeScript
TypeScript 代码片段通常使用较新的 ECMAScript import
和 export
关键字从其他模块导入对象,并声明这些对象在当前模块之外可用。Node.js 刚好在其最新版本中开始支持这些关键词。根据您正在使用(或希望支持)的 Node.js 版本,您可以重写导入和导出以使用旧语法。
可以将导入替换为对 require()
函数的调用。
可以将导出分配给 module.exports
对象。
注意
使用旧式导入和导出的另一种方法是使用 esm
对导入和导出进行分类后,就可以深入研究实际的代码了。您可能会遇到这些常用的 TypeScript 功能:
-
类型注释
-
接口定义
-
类型转换/强制转换
-
访问修饰符
可以为变量、类成员、函数参数和函数返回类型提供类型注释。对于变量、参数和成员,则通过在标识符后面加上冒号和类型来指定类型。函数返回值跟在函数签名后,由冒号和类型组成。
要将带类型注释的代码转换为 JavaScript,请移除冒号和类型。类成员在 JavaScript 中必须具有某些值;如果在 TypeScript 中只有类型注释,则将其设置为 undefined
。
在 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 功能。对于这些情况,我们建议使用 Sucrasetsc
那样报错。如果该变量在语法上是有效的,那么除了少数例外,Sucrase 都可以将其转换为 JavaScript。因此,其对于转换可能无法自行运行的代码片段尤为有价值。
迁移到 TypeScript
随着项目规模越来越大且越来越复杂,许多 JavaScript 开发人员开始转向使用 TypeScript
TypeScript“基于形状”的接口定义了对象中的必需和可选属性捆绑包(及其类型),可以编写代码时捕获常见错误,并让您的 IDE 更容易提供强大的自动完成功能和其他实时编码建议。
在 TypeScript 中编码确实涉及一个额外的步骤:即使用 TypeScript 编译器 tsc
来编译应用程序。对于常见的 AWS CDK 应用程序,编译最多需要几秒钟。
将现有 JavaScript AWS CDK 应用程序迁移到 TypeScript 的最简单方法是使用 cdk init app --language typescript
创建一个新的 TypeScript 项目,然后将源文件(以及任何其他必需文件,例如 AWS Lambda 函数源代码等资产)复制到新项目中。将您的 JavaScript 文件重命名为已 .ts
结尾,然后开始用 TypeScript 进行开发。