VVLL.net

Modules(模块)

日期:2024-08-22 09:58:31

关于术语的一点说明: 请务必注意一点,TypeScript 1.5里术语名已经发生了变化。 “内部模块”现在称做“命名空间”。 “外部模块”现在则简称为“模块”,这是为了与 ECMAScript 2015里的术语保持一致,(也就是说 module X { 相当于现在推荐的写法 namespace X {)。

  • 从ECMAScript 2015开始,JavaScript引入了模块的概念。TypeScript也沿用这个概念。

TypeScript与ECMAScript 2015一样,任何包含顶级import或者export的文件都被当成一个模块。相反地,如果一个文件不带有顶级的import或者export声明,那么它的内容被视为全局可见的(因此对模块也是可见的)。

TypeScript 模块是组织代码和实现代码重用的重要工具。模块帮助将代码分隔成逻辑单元,并允许这些单元之间进行清晰的依赖管理和代码共享。下面详细介绍 TypeScript 模块的概念、使用方法以及各种高级特性。

1. 模块基础

模块的定义:在 TypeScript 中,一个文件就是一个模块。模块中的代码默认是私有的,只有通过导出(export)的内容才能在其他模块中使用。

基本用法

导出和导入

导出 (Export):可以通过 export 关键字将变量、函数、类、接口等导出。

// math.ts
export const pi = 3.14;

export function add(a: number, b: number): number {
  return a + b;
}

export class Circle {
  constructor(public radius: number) {}
  area() {
    return pi * this.radius * this.radius;
  }
}

导入 (Import):可以通过 import 关键字从其他模块中导入导出的内容。

// main.ts
import { pi, add, Circle } from './math';

console.log(`Value of pi: ${pi}`);
console.log(`Add 2 + 3: ${add(2, 3)}`);
const myCircle = new Circle(5);
console.log(`Area of circle: ${myCircle.area()}`);

2. 默认导出(Default Exports)

默认导出是指一个模块可以有一个默认导出,使用 export default 语法。

// greet.ts
export default function greet(name: string): string {
  return `Hello, ${name}!`;
}

// main.ts
import greet from './greet';

console.log(greet("World")); // "Hello, World!"

说明:默认导出的内容在导入时不需要使用花括号 {},并且可以使用任意名字进行导入。

3. 重命名导入和导出

可以在导出和导入时对标识符进行重命名,以避免命名冲突或提升代码可读性。

导出时重命名

// constants.ts
const piValue = 3.14;
export { piValue as pi };

导入时重命名

// main.ts
import { pi as PI } from './constants';

console.log(PI); // 3.14

4. 重新导出(Re-Exporting)

重新导出可以将一个模块的所有内容或部分内容重新导出,以便在其他模块中使用。

重新导出所有内容

// allMath.ts
export * from './math';

重新导出部分内容

// someMath.ts
export { pi, add } from './math';

5. 动态导入

动态导入允许在运行时按需加载模块,使用 import() 语法。

// main.ts
async function loadModule() {
  const math = await import('./math');
  console.log(math.add(2, 3)); // 5
}

loadModule();

说明:动态导入返回一个 Promise,适合用于按需加载、代码分割和性能优化。

6. 命名空间(Namespaces)

命名空间用于将一组相关的代码组织在一起,它是 TypeScript 提供的另一种模块化方式,但在现代开发中更推荐使用模块(ES 模块)。

namespace MathUtils {
  export const pi = 3.14;

  export function add(a: number, b: number): number {
    return a + b;
  }

  export class Circle {
    constructor(public radius: number) {}
    area() {
      return pi * this.radius * this.radius;
    }
  }
}

// 使用命名空间
console.log(MathUtils.pi); // 3.14
console.log(MathUtils.add(2, 3)); // 5
const circle = new MathUtils.Circle(5);
console.log(circle.area()); // 78.5

7. 模块解析

TypeScript 提供了两种模块解析策略:经典(Classic)节点(Node)。默认情况下,TypeScript 使用节点解析策略。

节点解析策略:遵循 Node.js 的模块解析逻辑,支持相对路径和包路径导入。

// tsconfig.json
{
  "compilerOptions": {
    "moduleResolution": "node"
  }
}

8. 配置模块

tsconfig.json 文件中,可以通过 compilerOptions 配置模块相关选项。

{
  "compilerOptions": {
    "module": "esnext",
    "moduleResolution": "node",
    "baseUrl": "./",
    "paths": {
      "@utils/*": ["src/utils/*"]
    }
  }
}

说明:通过 baseUrlpaths 选项,可以配置路径别名,简化模块导入路径。

9. 高级用法示例

结合以上各种模块特性,可以实现复杂的模块组织和依赖管理。

示例

// utils.ts
export function log(message: string) {
  console.log(message);
}

// math.ts
import { log } from './utils';

export const pi = 3.14;

export function add(a: number, b: number): number {
  log(`Adding ${a} and ${b}`);
  return a + b;
}

// circle.ts
import { pi } from './math';

export class Circle {
  constructor(public radius: number) {}

  area() {
    return pi * this.radius * this.radius;
  }
}

// main.ts
import { add } from './math';
import { Circle } from './circle';

console.log(add(2, 3)); // "Adding 2 and 3" 5
const circle = new Circle(5);
console.log(circle.area()); // 78.5

总结

TypeScript 模块通过导出和导入机制帮助开发者将代码组织成逻辑单元,提高代码可维护性和重用性。默认导出、重命名导入导出、动态导入、命名空间等高级特性使得模块的使用更加灵活和强大。通过合理配置和使用这些特性,可以构建清晰、模块化和高效的 TypeScript 项目。

参考

标签