一、模块
模块在其自身的作用域里执行,而不是在全局作用域里;这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非你明确地使用export形式之一导出它们。
模块是自声明的;两个模块之间的关系是通过在文件级别上使用imports和exports建立的
1.导出
1.导出声明
任何声明(比如变量,函数,类,类型别名或接口)都能够通过添加export关键字来导出。
2.导出语句
export { ZipCodeValidator };export { ZipCodeValidator as mainValidator }
3.重新导出
2.导入
1.导入一个模块中的某个导出内容
import { ZipCodeValidator } from "./ZipCodeValidator";import { ZipCodeValidator as ZCV } from "./ZipCodeValidator";
2.将整个模块导入到一个变量,并通过它来访问模块的导出部分
import * as validator from "./ZipCodeValidator";
3.具有副作用的导入模块
// 这些模块可能没有任何的导出或用户根本就不关注它的导出import "./my-module.js";
4.默认导出
每个模块都可以有一个default导出。 默认导出使用default关键字标记;并且一个模块只能够有一个default导出。 需要使用一种特殊的导入形式来导入default导出。
3. export = 和 import = require()
CommonJS和AMD都有一个exports对象的概念,它包含了一个模块的所有导出内容。export =语法定义一个模块的导出对象。 它可以是类,接口,命名空间,函数或枚举。
若要导入一个使用了export =的模块时,必须使用TypeScript提供的特定语法import module = require("module")。
4.可选的模块加载和其它高级加载场景
编译器会检测是否每个模块都会在生成的JavaScript中用到。 如果一个模块标识符只在类型注解部分使用,并且完全没有在表达式中使用时,就不会生成require这个模块的代码。 省略掉没有用到的引用对性能提升是很有益的,并同时提供了选择性加载模块的能力。
Node.js里的动态模块加载
import { ZipCodeValidator as Zip } from "./ZipCodeValidator";
require.js里的动态模块加载
import * as Zip from "./ZipCodeValidator";
5.使用其它的JavaScript库
1.外部模块
使用module关键字并且把名字用引号括起来,方便之后import
declare module "path" {export function normalize(p: string): string;export function join(...paths: any[]): string;export let sep: string;}import { sep } from 'path'
2.模块声明通配符
5.创建模块结构指导
- 尽可能地在顶层导出
- 如果仅导出单个
class或function,使用export default - 如果要导出多个对象,把它们放在顶层里导出
- 使用命名空间导入模式当你要导出大量内容的时候
-
二、命名空间
“内部模块”现在叫做“命名空间。何使用
module关键字来声明一个内部模块的地方都应该使用namespace关键字来替换 ```typescript namespace Validation { export interface StringValidator {isAcceptable(s: string): boolean;
}
const lettersRegexp = /^[A-Za-z]+$/; const numberRegexp = /^[0-9]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {return lettersRegexp.test(s);}
} }
<a name="Yf4Ls"></a>## 1.分离到多文件当应用变得越来越大时,我们需要将代码分离到不同的文件中以便于维护。<a name="ZurpK"></a>### 1.多文件中的命名空间```typescriptnamespace Validation {export interface StringValidator {isAcceptable(s: string): boolean;}}
/// <reference path="Validation.ts" />namespace Validation {const lettersRegexp = /^[A-Za-z]+$/;export class LettersOnlyValidator implements StringValidator {isAcceptable(s: string) {return lettersRegexp.test(s);}}}
涉及到多文件时,我们必须确保所有编译后的代码都被加载了。
第一种方式,把所有的输入文件编译为一个输出文件,需要使用--outFile标记:
tsc --outFile sample.js Test.ts
第二种方式,我们可以编译每一个文件(默认方式),那么每个源文件都会对应生成一个JavaScript文件。 然后,在页面上通过<script>标签把所有生成的JavaScript文件按正确的顺序引进来
2.别名
import stringValidator = Validation.StringValidator;
三、命名空间和模块
1.使用命名空间
命名空间是位于全局命名空间下的一个普通的带有名字的JavaScript对象
2.使用模块
像命名空间一样,模块可以包含代码和声明。 不同的是模块可以声明它的依赖
3.命名空间和模块的陷阱
1.不必要的命名空间
如果你想把命名空间转换为模块,它可能会像下面这个文件一样
export namespace Shapes {export class Triangle { /* ... */ }export class Square { /* ... */ }}// 顶层的模块Shapes包裹了Triangle和Square。 对于使用它的人来说这是令人迷惑和讨厌的
模块文件本身已经是一个逻辑分组,并且它的名字是由导入这个模块的代码指定,所以没有必要为导出的对象增加额外的模块层。
四、模块解析
1. 相对 vs. 非相对模块导入
- 为你自己写的模块使用相对导入,这样能确保它们在运行时的相对位置
- 使用非相对路径来导入你的外部依赖
2.模块解析策略
共有两种可用的模块解析策略:Node和Classic。 你可以使用--moduleResolution标记指定使用哪种模块解析策略。 若未指定,那么在使用了--module AMD | System | ES2015时的默认值为Classic,其它情况时则为Node。Classic
这种策略以前是TypeScript默认的解析策略。 现在,它存在的理由主要是为了向后兼容。
相对导入的模块是相对于导入它的文件进行解析的。
对于非相对模块的导入,编译器则会从包含导入文件的目录开始依次向上级目录遍历,尝试定位匹配的声明文件。Node.js如何解析模块
https://typescript.bootcss.com/module-resolution.html#nodeTypeScript如何解析模块
TypeScript是模仿Node.js运行时的解析策略来在编译阶段定位模块定义文件。
3.Base URL
设置baseUrl来告诉编译器到哪里去查找模块。 所有非相对模块导入都会被当做相对于baseUrl。
baseUrl的值由以下两者之一决定:
