

# 使用 JavaScript 对 Amazon DynamoDB 进行编程
<a name="programming-with-javascript"></a>

本指南为想要结合 JavaScript 使用 Amazon DynamoDB 的程序员提供了指导。了解适用于 JavaScript 的 AWS SDK、可用的抽象层、配置连接、处理错误、定义重试策略、管理 keep-alive 等。

**Topics**
+ [关于 适用于 JavaScript 的 AWS SDK](#programming-with-javascript-about)
+ [使用适用于 JavaScript 的 AWS SDK V3](#programming-with-javascript-using-the-sdk)
+ [访问 JavaScript 文档](#programming-with-javascript-documentation)
+ [抽象层](#programming-with-javascript-abstraction-layers)
+ [使用编组实用程序函数](#programming-with-javascript-using-marshall-utility)
+ [读取项目](#programming-with-javascript-reading-items)
+ [有条件写入](#programming-with-javascript-conditional-writes)
+ [分页](#programming-with-javascript-pagination)
+ [指定配置](#programming-with-javascript-config)
+ [Waiter](#programming-with-javascript-waiters)
+ [错误处理](#programming-with-javascript-error-handling)
+ [日志记录](#programming-with-javascript-logging)
+ [注意事项](#programming-with-javascript-considerations)

## 关于 适用于 JavaScript 的 AWS SDK
<a name="programming-with-javascript-about"></a>

适用于 JavaScript 的 AWS SDK 支持使用浏览器脚本或 Node.js 访问 AWS 服务。本文档重点介绍最新版本的 SDK（V3）。适用于 JavaScript 的 AWS SDK V3 由 AWS 作为 [GitHub 上托管的开源项目](https://github.com/aws/aws-sdk-js-v3)进行维护。问题和特征请求是公开的，您可以在 GitHub 存储库的问题页面上进行访问。

JavaScript V2 与 V3 类似，但存在语法差异。V3 更加模块化，可以更轻松地发布较小的依赖项，并且具有一流的 TypeScript 支持。建议使用最新版本的 SDK。

## 使用适用于 JavaScript 的 AWS SDK V3
<a name="programming-with-javascript-using-the-sdk"></a>

您可以使用节点程序包管理器将 SDK 添加到 Node.js 应用程序中。以下示例展示了如何添加最常用的 SDK 包来使用 DynamoDB。
+ `npm install @aws-sdk/client-dynamodb`
+ `npm install @aws-sdk/lib-dynamodb`
+ `npm install @aws-sdk/util-dynamodb`

安装程序包会添加对 package.json 项目文件依赖项部分的引用。您可以选择使用更新的 ECMAScript 模块语法。有关这两种方法的更多详细信息，请参阅“注意事项”部分。

## 访问 JavaScript 文档
<a name="programming-with-javascript-documentation"></a>

通过以下资源开始学习 JavaScript 文档：
+ 访问[开发人员指南](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/welcome.html)，获取核心 JavaScript 文档。安装说明位于**设置**部分。
+ 访问 [API 参考](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/introduction/)文档，浏览所有可用的类和方法。
+ 适用于 JavaScript 的 SDK 除了 DynamoDB 之外还支持许多 AWS 服务。使用以下步骤查找 DynamoDB 的特定 API 覆盖范围：

  1. 从**服务**中选择 **DynamoDB 和库**。这记录了低级别客户端。

  1. 选择 **lib-dynamodb**。这记录了高级别客户端。这两个客户端代表两个不同的抽象层，您可以选择使用。有关抽象层的更多信息，请参阅以下部分。

## 抽象层
<a name="programming-with-javascript-abstraction-layers"></a>

适用于 JavaScript 的 SDK V3 有一个低级别客户端（`DynamoDBClient`）和一个高级别客户端（`DynamoDBDocumentClient`）。

**Topics**
+ [低级别客户端（`DynamoDBClient`）](#programming-with-javascript-low-level-client)
+ [高级别客户端（`DynamoDBDocumentClient`）](#programming-with-javascript-high-level-client)

### 低级别客户端（`DynamoDBClient`）
<a name="programming-with-javascript-low-level-client"></a>

低级别客户端不对底层线路议提供额外抽象。它使您可以完全控制通信的各个方面，但是由于没有抽象，您必须做一些使用 DynamoDB JSON 格式提供项目定义之类的事情。

如以下示例所示，使用这种格式，必须明确说明数据类型。*S* 表示字符串值，*N* 表示数字值。线路上的数字始终以标记为数字类型的字符串发送，以确保精度没有损失。低级别 API 调用有命名模式，如 `PutItemCommand` 和 `GetItemCommand`。

以下示例使用的是 `Item` 使用 DynamoDB JSON 定义的低级别客户端：

```
const { DynamoDBClient, PutItemCommand } = require("@aws-sdk/client-dynamodb");

const client = new DynamoDBClient({});

async function addProduct() {
  const params = {
    TableName: "products",
    Item: {
      "id": { S: "Product01" },
      "description": { S: "Hiking Boots" },
      "category": { S: "footwear" },
      "sku": { S: "hiking-sku-01" },
      "size": { N: "9" }
    }
  };

  try {
    const data = await client.send(new PutItemCommand(params));
    console.log('result : ' + JSON.stringify(data));
  } catch (error) {
    console.error("Error:", error);
  }
}
addProduct();
```

### 高级别客户端（`DynamoDBDocumentClient`）
<a name="programming-with-javascript-high-level-client"></a>

高级别 DynamoDB 文档客户端提供了内置的便利特征，例如无需手动编组数据，并允许使用标准 JavaScript 对象直接读取和写入数据。[`lib-dynamodb` 文档](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-lib-dynamodb/)列出了相关优点。

要实例化 `DynamoDBDocumentClient`，请构造一个低级别 `DynamoDBClient`，然后使用 `DynamoDBDocumentClient` 对其进行封装。这两个程序包的函数命名约定略有不同。例如，低级别使用 `PutItemCommand`，而高级别使用 `PutCommand`。不同的名称允许两组函数共存于同一个上下文中，从而允许您在同一个脚本中混合使用这两组函数。

```
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { DynamoDBDocumentClient, PutCommand } = require("@aws-sdk/lib-dynamodb");

const client = new DynamoDBClient({});

const docClient = DynamoDBDocumentClient.from(client);

async function addProduct() {
  const params = {
    TableName: "products",
    Item: {
      id: "Product01",
      description: "Hiking Boots",
      category: "footwear",
      sku: "hiking-sku-01",
      size: 9,
    },
  };

  try {
    const data = await docClient.send(new PutCommand(params));
    console.log('result : ' + JSON.stringify(data));
  } catch (error) {
    console.error("Error:", error);
  }
}

addProduct();
```

当您使用 `GetItem`、`Query` 或 `Scan` 等 API 操作读取项目时，使用模式是一致的。

## 使用编组实用程序函数
<a name="programming-with-javascript-using-marshall-utility"></a>

您可以使用低级别客户端，并自行编组或解组数据类型。实用程序包 [util-dynamodb](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-util-dynamodb/) 有一个接受 JSON 并生成 DynamoDB JSON 的 `marshall()` 实用程序函数，还有一个执行反向操作的 `unmarshall()` 函数。以下示例使用低级别客户端，数据编组由 `marshall()` 调用处理。

```
const { DynamoDBClient, PutItemCommand } = require("@aws-sdk/client-dynamodb");
const { marshall } = require("@aws-sdk/util-dynamodb");

const client = new DynamoDBClient({});

async function addProduct() {
  const params = {
    TableName: "products",
    Item: marshall({
      id: "Product01",
      description: "Hiking Boots",
      category: "footwear",
      sku: "hiking-sku-01",
      size: 9,
    }),
  };

  try {
    const data = await client.send(new PutItemCommand(params));
  } catch (error) {
    console.error("Error:", error);
  }
}
addProduct();
```

## 读取项目
<a name="programming-with-javascript-reading-items"></a>

要从 DynamoDB 中读取单个项目，请使用 `GetItem` API 操作。与 `PutItem` 命令类似，您可以选择使用低级别客户端，也可以选择使用高级别 Document 客户端。以下示例演示了如何使用高级别 Document 客户端检索项目。

```
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { DynamoDBDocumentClient, GetCommand } = require("@aws-sdk/lib-dynamodb");

const client = new DynamoDBClient({});

const docClient = DynamoDBDocumentClient.from(client);

async function getProduct() {
  const params = {
    TableName: "products",
    Key: {
      id: "Product01",
    },
  };

  try {
    const data = await docClient.send(new GetCommand(params));
    console.log('result : ' + JSON.stringify(data));
  } catch (error) {
    console.error("Error:", error);
  }
}

getProduct();
```

使用 `Query` API 操作读取多个项目。您可以使用低级别客户端或 Document 客户端。以下示例使用高级别 Document 客户端。

```
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const {
  DynamoDBDocumentClient,
  QueryCommand,
} = require("@aws-sdk/lib-dynamodb");

const client = new DynamoDBClient({});

const docClient = DynamoDBDocumentClient.from(client);

async function productSearch() {
  const params = {
    TableName: "products",
    IndexName: "GSI1",
    KeyConditionExpression: "#category = :category and begins_with(#sku, :sku)",
    ExpressionAttributeNames: {
      "#category": "category",
      "#sku": "sku",
    },
    ExpressionAttributeValues: {
      ":category": "footwear",
      ":sku": "hiking",
    },
  };

  try {
    const data = await docClient.send(new QueryCommand(params));
    console.log('result : ' + JSON.stringify(data));
  } catch (error) {
    console.error("Error:", error);
  }
}

productSearch();
```

## 有条件写入
<a name="programming-with-javascript-conditional-writes"></a>

DynamoDB 写入操作可以指定逻辑条件表达式，该表达式的计算结果必须为 true 才能继续写入。如果条件的计算结果不是 true，则写入操作会引发异常。条件表达式可以检查项目是否已经存在，或者其属性是否符合某些约束。

`ConditionExpression = "version = :ver AND size(VideoClip) < :maxsize" `

当条件表达式失败时，您可以使用 `ReturnValuesOnConditionCheckFailure` 请求错误响应中包含不满足条件的项目，以推断问题出在哪里。有关更多详细信息，请参阅[使用 Amazon DynamoDB 处理高并发场景中的条件写入错误](https://aws.amazon.com/blogs/database/handle-conditional-write-errors-in-high-concurrency-scenarios-with-amazon-dynamodb/)。

```
try {
      const response = await client.send(new PutCommand({
          TableName: "YourTableName",
          Item: item,
          ConditionExpression: "attribute_not_exists(pk)",
          ReturnValuesOnConditionCheckFailure: "ALL_OLD"
      }));
  } catch (e) {
      if (e.name === 'ConditionalCheckFailedException') {
          console.log('Item already exists:', e.Item);
      } else {
          throw e;
      }
  }
```

[JavaScript SDK V3 文档](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/javascript_dynamodb_code_examples.html)和 [DynamoDB-SDK-Examples GitHub 存储库](https://github.com/aws-samples/aws-dynamodb-examples/tree/master/examples/SDK/node.js)中提供了更多显示 JavsScript SDK V3 使用情况其它方面的代码示例。

## 分页
<a name="programming-with-javascript-pagination"></a>

**Topics**
+ [使用 `paginateScan` 便捷方法](#using-the-paginatescan-convenience-method)

诸如 `Scan` 或 `Query` 之类的读取请求可能会返回数据集中的多个项目。如果您使用 `Limit` 参数执行 `Scan` 或 `Query`，那么一旦系统读取许多项目，就会发送部分响应，您需要分页才能检索其它项目。

系统每次请求最多只能读取 1 MB 的数据。如果包含 `Filter` 表达式，系统仍将从磁盘读取最多 1 MB 的数据，但会返回与筛选条件匹配的相应 MB 的项目。筛选操作可能会针对一个页面返回 0 个项目，但在搜索用尽之前仍需要进一步分页。

您应该在响应中查找 `LastEvaluatedKey`，并在后续请求中将其用作 `ExclusiveStartKey` 参数，才能继续检索数据。如以下示例所示，这用作书签。

**注意**  
样本在首次迭代时传递一个空 `lastEvaluatedKey` 作为 `ExclusiveStartKey`，这是允许的。

使用 `LastEvaluatedKey` 的示例：

```
const { DynamoDBClient, ScanCommand } = require("@aws-sdk/client-dynamodb");

const client = new DynamoDBClient({});

async function paginatedScan() {
  let lastEvaluatedKey;
  let pageCount = 0;

  do {
    const params = {
      TableName: "products",
      ExclusiveStartKey: lastEvaluatedKey,
    };

    const response = await client.send(new ScanCommand(params));
    pageCount++;
    console.log(`Page ${pageCount}, Items:`, response.Items);
    lastEvaluatedKey = response.LastEvaluatedKey;
  } while (lastEvaluatedKey);
}

paginatedScan().catch((err) => {
  console.error(err);
});
```

### 使用 `paginateScan` 便捷方法
<a name="using-the-paginatescan-convenience-method"></a>



SDK 提供了名为 `paginateScan` 和 `paginateQuery` 的便捷方法，这些方法可以为您完成这项工作，并在后台重复请求。使用标准 `Limit` 参数指定每次请求可读取的最大项目数。

```
const { DynamoDBClient, paginateScan } = require("@aws-sdk/client-dynamodb");

const client = new DynamoDBClient({});

async function paginatedScanUsingPaginator() {
  const params = {
    TableName: "products",
    Limit: 100
  };

  const paginator = paginateScan({client}, params);

  let pageCount = 0;

  for await (const page of paginator) {
    pageCount++;
    console.log(`Page ${pageCount}, Items:`, page.Items);
  }
}

paginatedScanUsingPaginator().catch((err) => {
  console.error(err);
});
```

**注意**  
除非表很小，否则不建议定期执行全表扫描。

## 指定配置
<a name="programming-with-javascript-config"></a>

**Topics**
+ [超时配置](#programming-with-javascript-config-timeouts)
+ [keep-alive 配置](#programming-with-javascript-config-keep-alive)
+ [重试配置](#programming-with-javascript-config-retries)

设置 `DynamoDBClient` 时，您可以通过将配置对象传递给构造函数来指定各种配置覆盖。例如，如果调用上下文或要使用的端点 URL 尚不知道要连接的区域，则可以指定要连接的区域。如果您希望出于开发目的选择 DynamoDB Local 实例，这会很有用。

```
const client = new DynamoDBClient({
  region: "eu-west-1",
  endpoint: "http://localhost:8000",
});
```

### 超时配置
<a name="programming-with-javascript-config-timeouts"></a>

DynamoDB 使用 HTTPS 进行客户端-服务器通信。您可以通过提供 `NodeHttpHandler` 对象来控制 HTTP 层的某些方面。例如，您可以调整密钥超时值 `connectionTimeout` 和 `requestTimeout`。`connectionTimeout` 是客户端在尝试建立连接时，在放弃之前等待的最大持续时间（以毫秒为单位）。

`requestTimeout` 定义了发送请求后客户端将等待响应的时间，也以毫秒为单位。两者的默认值均为零，这意味着超时已禁用，如果响应未到达，客户端的等待时间将没有限制。您应该将超时设置为合理的值，以便在出现网络问题时，请求将出错并可以启动新的请求。例如：

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { NodeHttpHandler } from "@smithy/node-http-handler";

const requestHandler = new NodeHttpHandler({
  connectionTimeout: 2000,
  requestTimeout: 2000,
});

const client = new DynamoDBClient({
  requestHandler
});
```

**注意**  
提供的示例使用 [Smithy](https://smithy.io/2.0/index.html) 导入。Smithy 是一种用于定义服务和 SDK 的语言，是开源的，由 AWS 维护。

除了配置超时值外，您还可以设置最大套接字数，这样可以增加每个源的并发连接数。开发人员指南包含[有关配置 `maxSockets` 参数的详细信息](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html)。

### keep-alive 配置
<a name="programming-with-javascript-config-keep-alive"></a>

使用 HTTPS 时，第一个请求总是需要一些往来通信才能建立安全连接。HTTP Keep-Alive 允许后续请求重用已经建立的连接，从而提高请求的效率并降低延迟。JavaScript V3 默认启用 HTTP Keep-Alive。

空闲连接可以保持活动状态的时间是有限制的。如果您有一个空闲的连接，但希望下次请求使用已经建立的连接，可以考虑定期发送请求，比如每分钟发送一次。

**注意**  
请注意，在较旧的 SDK V2 中，keep-alive 默认处于关闭状态，这意味着每个连接在使用后都会立即关闭。如果使用 V2，则您可以覆盖此设置。

### 重试配置
<a name="programming-with-javascript-config-retries"></a>

当 SDK 收到错误响应且 SDK 确定错误可以恢复时，例如节流异常或临时服务异常，它将会重试。您作为调用方，看不到这种情况的发生，只是您可能会注意到，请求花了更长时间才成功完成。

默认情况下，适用于 JavaScript 的 SDK V3 总共会发出 3 个请求，然后才放弃并将错误传递到调用上下文。您可以调整这些重试的次数和频率。

`DynamoDBClient` 构造函数接受一个 `maxAttempts` 设置，该设置限制了将要发生的尝试次数。以下示例将值从默认的 3 提高到总计 5。如果您将其设置为 0 或 1，则表示您不想进行任何自动重试，而是想在 catch 块中自己处理任何可恢复的错误。

```
const client = new DynamoDBClient({
  maxAttempts: 5,
});
```

您还可以使用自定义重试策略来控制重试的时间。为此，请导入 `util-retry` 实用程序包并创建一个自定义回退函数，该函数根据当前的重试计数计算重试之间的等待时间。

下面的示例表明，如果第一次尝试失败，最多可以尝试 5 次，延迟时间为 15、30、90 和 360 毫秒。自定义回退函数 ` calculateRetryBackoff` 通过接受重试尝试次数（首次重试从 1 开始）来计算延迟，并返回等待该请求的毫秒数。

```
const { ConfiguredRetryStrategy } = require("@aws-sdk/util-retry");

const calculateRetryBackoff = (attempt) => {
  const backoffTimes = [15, 30, 90, 360];
  return backoffTimes[attempt - 1] || 0;
};

const client = new DynamoDBClient({
  retryStrategy: new ConfiguredRetryStrategy(
    5, // max attempts.
    calculateRetryBackoff // backoff function.
  ),
});
```

## Waiter
<a name="programming-with-javascript-waiters"></a>

DynamoDB 客户端包含两个有用的 [Waiter 函数](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/dynamodb/wait/index.html#cli-aws-dynamodb-wait)，当您希望代码等待表修改完成后再继续执行时，可以在创建、修改或删除表时使用这些函数。例如，您可以部署表，调用 `waitUntilTableExists` 函数，代码将会阻塞，直到表变为 **ACTIVE** 状态。Waiter 函数每 20 秒在内部使用 `describe-table` 轮询一次 DynamoDB 服务。

```
import {waitUntilTableExists, waitUntilTableNotExists} from "@aws-sdk/client-dynamodb";

… <create table details>

const results = await waitUntilTableExists({client: client, maxWaitTime: 180}, {TableName: "products"});
if (results.state == 'SUCCESS') {
  return results.reason.Table
}
console.error(`${results.state} ${results.reason}`);
```

仅当 `waitUntilTableExists` 特征可以执行显示表状态为 **ACTIVE** 的 `describe-table` 命令时，该特征才会返回控制权。这样可以确保您能够使用 `waitUntilTableExists` 等待创建完成以及诸如添加 GSI 索引之类的修改完成，这些修改可能需要一些时间才能应用，然后表才会恢复为 **ACTIVE** 状态。

## 错误处理
<a name="programming-with-javascript-error-handling"></a>

在此处的早期示例中，我们已经广泛地发现了所有错误。但是，在实际应用中，区分各种错误类型并实现更精确的错误处理非常重要。

DynamoDB 错误响应包含元数据，其中包括错误名称。您可以捕获错误，然后与错误条件中可能的字符串名称进行匹配，来确定如何继续。对于服务器端错误，您可以利用错误类型由 `@aws-sdk/client-dynamodb` 程序包导出的 `instanceof` 运算符，来高效地管理错误处理。

需要注意的是，这些错误只有在所有重试都用尽后才会显示。如果重试错误并最终成功调用，则从代码的角度来看，没有错误，只是延迟略有增加。重试将在 Amazon CloudWatch 图表中显示为失败的请求，例如节流请求或错误请求。如果客户端达到最大重试次数，它将放弃并引发异常。客户端以此表明它不会重试。

下面是一个代码段，用于捕获错误并根据返回的错误类型采取行动。

```
import {
  ResourceNotFoundException
  ProvisionedThroughputExceededException,
  DynamoDBServiceException,
} from "@aws-sdk/client-dynamodb";

try {
  await client.send(someCommand);
} catch (e) {
    if (e instanceof ResourceNotFoundException) {
      // Handle ResourceNotFoundException
    } else if (e instanceof ProvisionedThroughputExceededException) {
      // Handle ProvisionedThroughputExceededException
    } else if (e instanceof DynamoDBServiceException) {
      // Handle DynamoDBServiceException
    } else {
      // Other errors such as those from the SDK
      if (e.name === "TimeoutError") {
        // Handle SDK TimeoutError.
      } else {
        // Handle other errors.
      }
    }
}
```

有关常见错误字符串，请参阅《DynamoDB 开发人员指南》**中的[DynamoDB 错误处理](Programming.Errors.md)。任何特定 API 调用可能出现的确切错误都可以在该 API 调用的文档中找到，例如[查询 API 文档](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html)。

错误的元数据包括其它属性，具体视错误而定。对于 ` TimeoutError`，元数据包括尝试次数和 `totalRetryDelay`，如下所示。

```
{
  "name": "TimeoutError",
  "$metadata": {
    "attempts": 3,
    "totalRetryDelay": 199
  }
}
```

如果您管理自己的重试策略，则需要区分节流和错误：
+ **节流**（由 ` ProvisionedThroughputExceededException` 或 `ThrottlingException` 表示）表示服务运行正常，它会通知您已超出 DynamoDB 表或分区的读取或写入容量。每过一毫秒，就会有多一点的读取或写入容量可用，因此您可以快速重试，例如每 50 毫秒重试一次，尝试访问新释放的容量。

   使用节流，您并不特别需要指数回退，因为节流属于轻量级，DynamoDB 可以返回，而且不会向您收取每次请求的费用。指数回退会将更长的延迟分配给已经等待最长时间的客户端线程，从统计学上讲，将超越 p50 和 p99。
+ **错误**（由 ` InternalServerError` 或 `ServiceUnavailable` 等表示）表示服务存在暂时性问题，可能是整个表，也可能只是您正在读取或写入的分区。使用错误，您可以在重试前暂停更长时间，例如 250 毫秒或 500 毫秒，并使用抖动来错开重试。

## 日志记录
<a name="programming-with-javascript-logging"></a>

开启日志记录功能以获取有关 SDK 正在执行的操作的更多详细信息。您可以在 `DynamoDBClient` 上设置参数，如以下示例所示。更多日志信息将显示在控制台中，包括状态码和已用容量等元数据。如果您在终端窗口中本地运行代码，则日志会显示于此。如果您在 AWS Lambda 中运行代码，并且设置了 Amazon CloudWatch Logs，则控制台输出将写入于此。

```
const client = new DynamoDBClient({
  logger: console
});
```

您还可以挂钩到内部 SDK 活动，并在某些事件发生时执行自定义日志记录。以下示例使用客户端的 `middlewareStack` 拦截从 SDK 发送的每个请求，并在请求发生时将其记录下来。

```
const client = new DynamoDBClient({});

client.middlewareStack.add(
  (next) => async (args) => {
    console.log("Sending request from AWS SDK", { request: args.request });
    return next(args);
  },
  {
    step: "build",
    name: "log-ddb-calls",
  }
);
```

`MiddlewareStack` 提供了用于观察和控制 SDK 行为的强大钩子。有关更多信息，请参阅博客 [Introducing Middleware Stack in Modular 适用于 JavaScript 的 AWS SDK](https://aws.amazon.com/blogs/developer/middleware-stack-modular-aws-sdk-js/)。

## 注意事项
<a name="programming-with-javascript-considerations"></a>

在您的项目中实施适用于 JavaScript 的 AWS SDK 时，需要考虑以下其它因素。

**模块系统**  
该 SDK 支持两个模块系统，CommonJS 和 ES (ECMAScript)。CommonJS 使用 `require` 函数，而 ES 使用 `import` 关键字。  

1. **Common JS** – `const { DynamoDBClient, PutItemCommand } = require("@aws-sdk/client-dynamodb");`

1. **ES (ECMAScript** – `import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";`
项目类型指示了要使用的模块系统，并在 package.json 文件的类型部分中指定。默认为 CommonJS。使用 `"type": "module"` 指示 ES 项目。如果您有一个使用 CommonJS 程序包格式的现有 Node.JS 项目，您仍然可以通过使用 .mjs 扩展名命名函数文件，来使用更现代的 SDK V3 Import 语法添加函数。这将允许将代码文件视为 ES (ECMAScript)。

**异步操作**  
您将看到许多使用回调和承诺来处理 DynamoDB 操作结果的代码示例。在现代 JavaScript 中，不再需要这种复杂性，开发人员可以利用更简洁、可读性更好的异步/等待语法执行异步操作。

**Web 浏览器运行时**  
使用 React 或 React Native 构建的网络和移动开发人员可以在他们的项目中使用适用于 JavaScript 的 SDK。在较早的 SDK V2 中，Web 开发人员必须将完整的 SDK 加载到浏览器中，并引用托管在 https://sdk.amazonaws.com/js/ 上的 SDK 图片。  
在 V3 中，您可以使用 Webpack 将所需的 V3 客户端模块和所有必需的 JavaScript 函数捆绑到一个 JavaScript 文件中，然后将其添加到 HTML 页面 `<head>` 的脚本标签中，如 SDK 文档的[浏览器脚本入门](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/getting-started-browser.html)部分所述。

**DAX 数据面板操作**  
适用于 JavaScript 的 SDK V3 支持 Amazon DynamoDB Streams Accelerator（DAX）数据面板操作。