Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zh-cn: init localization of modules #6

Merged
merged 2 commits into from
Nov 6, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 67 additions & 74 deletions docs/documentation/zh/handbook-v2/Modules.md
Original file line number Diff line number Diff line change
@@ -1,61 +1,56 @@
---
title: Modules
title: 模块
layout: docs
permalink: /zh/docs/handbook/2/modules.html
oneline: "How JavaScript handles communicating across file boundaries."
oneline: "JavaScript 处理跨文件通信的方式。"
---

JavaScript has a long history of different ways to handle modularizing code.
Having been around since 2012, TypeScript has implemented support for a lot of these formats, but over time the community and the JavaScript specification has converged on a format called ES Modules (or ES6 modules). You might know it as the `import`/`export` syntax.
JavaScript 历来具有多种处理代码模块化的方式。TypeScript 自 2012 年问世以来,已经实现了对这其中很多格式的支持。但随着时间的推移,社区和 JavaScript 规范已经趋于使用一种称为 ES 模块(或 ES6 模块)的格式。它使用的是 `import`/`export` 语法。

ES Modules was added to the JavaScript spec in 2015, and by 2020 had broad support in most web browsers and JavaScript runtimes.
ES 模块在 2015 年被添加到 JavaScript 规范中,并且截至 2020 年已经在大多数 Web 浏览器和 JavaScript 运行时中得到广泛支持。

For focus, the handbook will cover both ES Modules and its popular pre-cursor CommonJS `module.exports =` syntax, and you can find information about the other module patterns in the reference section under [Modules](/docs/handbook/modules.html).
本手册将重点介绍 ES 模块及其流行的前身 CommonJS `module.exports =` 语法,你可以在参考部分的 [模块](/docs/handbook/modules.html) 下找到其他模块模式的信息。

## How JavaScript Modules are Defined
## JavaScript 模块的定义方式

In TypeScript, just as in ECMAScript 2015, any file containing a top-level `import` or `export` is considered a module.
TypeScript 中(与 ECMAScript 2015 一样),任何包含顶级 `import` `export` 声明的文件都被视为一个模块。

Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well).
相反,如果一个文件没有任何顶级导入或导出声明,它将被视为一个脚本,其内容在全局范围内可用(因此也可用于模块)。

Modules are executed within their own scope, not in the global scope.
This means that variables, functions, classes, etc. declared in a module are not visible outside the module unless they are explicitly exported using one of the export forms.
Conversely, to consume a variable, function, class, interface, etc. exported from a different module, it has to be imported using one of the import forms.
模块在它们自己的作用域中执行,而不是在全局作用域中执行。这意味着在模块中声明的变量、函数、类等在模块外部是不可见的,除非它们使用其中某种导出形式进行了显式导出。相反,要使用从其他模块导出的变量、函数、类、接口等,必须使用某种导入形式进行导入。

## Non-modules
## 非模块文件

Before we start, it's important to understand what TypeScript considers a module.
The JavaScript specification declares that any JavaScript files without an `import` declaration, `export`, or top-level `await` should be considered a script and not a module.
在开始之前,我们有必要了解一下 TypeScript 将什么当作模块。JavaScript 规范声明,任何没有 `import` 声明、`export` 声明或顶级 `await` 的 JavaScript 文件都应被视为脚本而不是模块。

在脚本文件中,声明的变量和类型处于共享的全局作用域。你应该要么使用 [`outFile`](/tsconfig#outFile) 编译器选项将多个输入文件合并为一个输出文件,要么在 HTML 代码中使用多个 `<script>` 标签按正确的顺序加载这些文件。

Inside a script file variables and types are declared to be in the shared global scope, and it's assumed that you'll either use the [`outFile`](/tsconfig#outFile) compiler option to join multiple input files into one output file, or use multiple `<script>` tags in your HTML to load these files (in the correct order!).

If you have a file that doesn't currently have any `import`s or `export`s, but you want to be treated as a module, add the line:
如果你有一个当前没有任何 `import` 或 `export` 声明的文件,但你希望将其视为一个模块,可以添加以下代码行:

```ts twoslash
export {};
```

which will change the file to be a module exporting nothing. This syntax works regardless of your module target.
这将使文件成为一个不导出任何内容的模块。无论你的模块目标是什么,这种语法都适用。

## Modules in TypeScript
## TypeScript 中的模块

<blockquote class='bg-reading'>
<p>Additional Reading:<br />
<a href='https://exploringjs.com/impatient-js/ch_modules.html#overview-syntax-of-ecmascript-modules'>Impatient JS (Modules)</a><br/>
<a href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules'>MDN: JavaScript Modules</a><br/>
<p>延申阅读:<br />
<a href='https://exploringjs.com/impatient-js/ch_modules.html#overview-syntax-of-ecmascript-modules'>Impatient JSModules</a><br/>
<a href='https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Modules'>MDNJavaScript 模块</a><br/>
</p>
</blockquote>

There are three main things to consider when writing module-based code in TypeScript:
在用 TypeScript 编写基于模块的代码时,主要有三个要考虑的方面:

- **Syntax**: What syntax do I want to use to import and export things?
- **Module Resolution**: What is the relationship between module names (or paths) and files on disk?
- **Module Output Target**: What should my emitted JavaScript module look like?
- **语法**:我希望使用什么语法来导入和导出内容?
- **模块解析**:模块名称(或路径)与磁盘上的文件之间的关系是什么?
- **模块输出目标**:我的输出 JavaScript 模块应该是什么样子的?

### ES Module Syntax
### ES 模块语法

A file can declare a main export via `export default`:
一个文件可以通过 `export default` 来声明主要的导出项:

```ts twoslash
// @filename: hello.ts
Expand All @@ -64,7 +59,7 @@ export default function helloWorld() {
}
```

This is then imported via:
然后可以通过以下方式进行导入:

```ts twoslash
// @filename: hello.ts
Expand All @@ -77,7 +72,7 @@ import helloWorld from "./hello.js";
helloWorld();
```

In addition to the default export, you can have more than one export of variables and functions via the `export` by omitting `default`:
除了默认导出之外,你还可以通过省略 `default` 来导出多个变量和函数:

```ts twoslash
// @filename: maths.ts
Expand All @@ -93,7 +88,7 @@ export function absolute(num: number) {
}
```

These can be used in another file via the `import` syntax:
可以通过 `import` 语法在另一个文件中使用这些导出项:

```ts twoslash
// @filename: maths.ts
Expand All @@ -114,9 +109,9 @@ const absPhi = absolute(phi);
// ^?
```

### Additional Import Syntax
### 更多的导入语法

An import can be renamed using a format like `import {old as new}`:
可以使用以下格式将导入进行重命名:`import {old as new}`

```ts twoslash
// @filename: maths.ts
Expand All @@ -129,7 +124,7 @@ console.log(π);
// ^?
```

You can mix and match the above syntax into a single `import`:
你可以将上述语法混合使用在单个 `import` 语句中:

```ts twoslash
// @filename: maths.ts
Expand All @@ -146,7 +141,7 @@ console.log(π);
// ^?
```

You can take all of the exported objects and put them into a single namespace using `* as name`:
你可以使用声明 `* as name` 将所有导出的对象放入单个命名空间中:

```ts twoslash
// @filename: maths.ts
Expand All @@ -167,7 +162,7 @@ const positivePhi = math.absolute(math.phi);
// ^?
```

You can import a file and _not_ include any variables into your current module via `import "./file"`:
通过使用 `import "./file"`,你可以导入一个文件,而不将任何变量包含在当前模块中:

```ts twoslash
// @filename: maths.ts
Expand All @@ -179,11 +174,11 @@ import "./maths.js";
console.log("3.14");
```

In this case, the `import` does nothing. However, all of the code in `maths.ts` was evaluated, which could trigger side-effects which affect other objects.
在这种情况下,`import` 没有任何作用。然而,`maths.ts` 中的所有代码都被执行,这可能触发影响其他对象的副作用。

#### TypeScript Specific ES Module Syntax
#### TypeScript 特定的 ES 模块语法

Types can be exported and imported using the same syntax as JavaScript values:
类型可以使用与 JavaScript 相同的语法进行导出和导入:

```ts twoslash
// @filename: animal.ts
Expand All @@ -199,11 +194,11 @@ import { Cat, Dog } from "./animal.js";
type Animals = Cat | Dog;
```

TypeScript has extended the `import` syntax with two concepts for declaring an import of a type:
TypeScript 通过两个用来声明类型导入的概念,扩展了 `import` 语法:

###### `import type`

Which is an import statement which can _only_ import types:
这是一个_仅_能导入类型的导入语句:

```ts twoslash
// @filename: animal.ts
Expand All @@ -221,9 +216,9 @@ import type { createCatName } from "./animal.js";
const name = createCatName();
```

###### Inline `type` imports
###### 内联 `type` 导入

TypeScript 4.5 also allows for individual imports to be prefixed with `type` to indicate that the imported reference is a type:
TypeScript 4.5 还允许在个别导入中使用 `type` 前缀,以指示被导入的引用是一个类型:

```ts twoslash
// @filename: animal.ts
Expand All @@ -238,11 +233,11 @@ export type Animals = Cat | Dog;
const name = createCatName();
```

Together these allow a non-TypeScript transpiler like Babel, swc or esbuild to know what imports can be safely removed.
通过这些语法,非 TypeScript 的转译器(如 Babelswc esbuild)可以知道哪些导入可以安全地移除。

#### ES Module Syntax with CommonJS Behavior
#### 具有 CommonJS 行为的 ES 模块语法

TypeScript has ES Module syntax which _directly_ correlates to a CommonJS and AMD `require`. Imports using ES Module are _for most cases_ the same as the `require` from those environments, but this syntax ensures you have a 1 to 1 match in your TypeScript file with the CommonJS output:
TypeScript 具有 ES 模块语法,它与 CommonJS AMD `require` _直接_对应。使用 ES 模块进行导入在大多数情况下与这些环境中的 `require` 相同,但是此语法确保你的 TypeScript 文件与 CommonJS 输出保持一对一的匹配:

```ts twoslash
/// <reference types="node" />
Expand All @@ -252,15 +247,15 @@ import fs = require("fs");
const code = fs.readFileSync("hello.ts", "utf8");
```

You can learn more about this syntax in the [modules reference page](/docs/handbook/modules.html#export--and-import--require).
你可以在 [模块参考页面](/docs/handbook/modules.html#export--and-import--require) 了解更多关于此语法的信息。

## CommonJS Syntax
## CommonJS 语法

CommonJS is the format which most modules on npm are delivered in. Even if you are writing using the ES Modules syntax above, having a brief understanding of how CommonJS syntax works will help you debug easier.
CommonJS 是大多数 npm 模块采用的格式。即使你使用上面的 ES 模块语法编写代码,了解 CommonJS 语法的基本原理也将有助于更轻松地进行调试。

#### Exporting
#### 导出

Identifiers are exported via setting the `exports` property on a global called `module`.
通过在名为 `module` 的全局变量上设置 `exports` 属性,可以导出标识符。

```ts twoslash
/// <reference types="node" />
Expand All @@ -278,7 +273,7 @@ module.exports = {
};
```

Then these files can be imported via a `require` statement:
然后可以通过 `require` 语句导入这些文件:

```ts twoslash
// @module: commonjs
Expand All @@ -302,7 +297,7 @@ maths.pi;
// ^?
```

Or you can simplify a bit using the destructuring feature in JavaScript:
或者你可以使用 JavaScript 的解构(destructuring)特性来简化代码:

```ts twoslash
// @module: commonjs
Expand All @@ -326,34 +321,32 @@ squareTwo;
// ^?
```

### CommonJS and ES Modules interop
### CommonJS ES 模块的互操作性

There is a mis-match in features between CommonJS and ES Modules regarding the distinction between a default import and a module namespace object import. TypeScript has a compiler flag to reduce the friction between the two different sets of constraints with [`esModuleInterop`](/tsconfig#esModuleInterop).
CommonJS ES 模块之间存在一个特性差异,即默认导入和模块命名空间对象导入之间的区别。TypeScript 具有一个编译器标志 [`esModuleInterop`](/tsconfig#esModuleInterop),用于减少这两组不同约束之间的摩擦。

## TypeScript's Module Resolution Options
## TypeScript 的模块解析选项

Module resolution is the process of taking a string from the `import` or `require` statement, and determining what file that string refers to.
模块解析是指接收 `import` `require` 语句中的字符串,并确定该字符串所指向的文件的过程。

TypeScript includes two resolution strategies: Classic and Node. Classic, the default when the compiler option [`module`](/tsconfig#module) is not `commonjs`, is included for backwards compatibility.
The Node strategy replicates how Node.js works in CommonJS mode, with additional checks for `.ts` and `.d.ts`.
TypeScript 包括两种解析策略:经典解析和 Node 解析。经典解析在编译器选项 [`module`](/tsconfig#module) 不是 `commonjs` 时是默认策略,它用于向后兼容。Node 解析策略复制了 Node.js 在 CommonJS 模式下的工作方式,并额外检查 `.ts` 和 `.d.ts` 文件。

There are many TSConfig flags which influence the module strategy within TypeScript: [`moduleResolution`](/tsconfig#moduleResolution), [`baseUrl`](/tsconfig#baseUrl), [`paths`](/tsconfig#paths), [`rootDirs`](/tsconfig#rootDirs).
有许多 TSConfig 标志会影响 TypeScript 内部的模块策略,包括 [`moduleResolution`](/tsconfig#moduleResolution)[`baseUrl`](/tsconfig#baseUrl)[`paths`](/tsconfig#paths)[`rootDirs`](/tsconfig#rootDirs)

For the full details on how these strategies work, you can consult the [Module Resolution](/docs/handbook/module-resolution.html).
要了解这些策略的详细信息,可以参考 [模块解析](/docs/handbook/module-resolution.html)

## TypeScript's Module Output Options
## TypeScript 的模块输出选项

There are two options which affect the emitted JavaScript output:
有两个选项会影响生成的 JavaScript 输出:

- [`target`](/tsconfig#target) which determines which JS features are downleveled (converted to run in older JavaScript runtimes) and which are left intact
- [`module`](/tsconfig#module) which determines what code is used for modules to interact with each other
- [`target`](/tsconfig#target):确定哪些 JS 特性会被降级(转换为在较旧的 JavaScript 运行环境中运行的代码),哪些保持不变
- [`module`](/tsconfig#module):确定用于模块间交互的代码

Which [`target`](/tsconfig#target) you use is determined by the features available in the JavaScript runtime you expect to run the TypeScript code in. That could be: the oldest web browser you support, the lowest version of Node.js you expect to run on or could come from unique constraints from your runtime - like Electron for example.
你选择哪个 [target](/tsconfig#target),要看你的 TypeScript 代码要在哪种 JavaScript 运行时环境下执行。这可能取决于:你要兼容的最老的网页浏览器,你要使用的 Node.js 的最低版本,或者你的运行时环境有什么特殊的限制 - 比如 Electron

All communication between modules happens via a module loader, the compiler option [`module`](/tsconfig#module) determines which one is used.
At runtime the module loader is responsible for locating and executing all dependencies of a module before executing it.
所有模块之间的通信都是通过模块加载器进行的,编译器选项 [`module`](/tsconfig#module) 确定使用哪个模块加载器。在运行时,模块加载器负责在执行模块之前定位和执行它的所有依赖项。

For example, here is a TypeScript file using ES Modules syntax, showcasing a few different options for [`module`](/tsconfig#module):
例如,以下是一个使用 ES 模块语法的 TypeScript 文件,展示了几种不同的 [`module`](/tsconfig#module) 选项:

```ts twoslash
// @filename: constants.ts
Expand Down Expand Up @@ -398,10 +391,10 @@ import { valueOfPi } from "./constants.js";
export const twoPi = valueOfPi * 2;
```

> Note that ES2020 is effectively the same as the original `index.ts`.
> 请注意,ES2020 在功能上与原始的 `index.ts` 相同。

You can see all of the available options and what their emitted JavaScript code looks like in the [TSConfig Reference for `module`](/tsconfig#module).
你可以在 [TSConfig `module` 配置参考页面](/tsconfig#module) 中查看所有可用选项及其生成的 JavaScript 代码。

## TypeScript namespaces
## TypeScript 命名空间

TypeScript has its own module format called `namespaces` which pre-dates the ES Modules standard. This syntax has a lot of useful features for creating complex definition files, and still sees active use [in DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped). While not deprecated, the majority of the features in namespaces exist in ES Modules and we recommend you use that to align with JavaScript's direction. You can learn more about namespaces in [the namespaces reference page](/docs/handbook/namespaces.html).
TypeScript 有自己的模块格式,称为 `命名空间`,它早于 ES 模块的标准化。这种语法具有许多用于创建复杂定义文件的有用功能,并且仍然在 [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) 中得到广泛使用。虽然没有被弃用,但大多数命名空间中的功能也存在于 ES 模块中,因此我们建议你使用 ES 模块,以与 JavaScript 的发展方向保持一致。你可以在[命名空间参考页面](/docs/handbook/namespaces.html)了解更多关于命名空间的信息。
Loading