ES模块 #

一、模块概述 #

Deno使用标准的ES模块(ES Modules)作为模块系统,这是JavaScript的官方模块标准。与Node.js的CommonJS不同,ES模块使用 importexport 语法。

二、导出(export) #

2.1 命名导出 #

typescript
// utils.ts

// 导出变量
export const PI = 3.14159;

// 导出函数
export function add(a: number, b: number): number {
  return a + b;
}

// 导出类
export class Calculator {
  add(a: number, b: number): number {
    return a + b;
  }
}

// 导出接口
export interface User {
  id: number;
  name: string;
}

// 导出类型别名
export type ID = string | number;

2.2 导出语句 #

typescript
// math.ts

const PI = 3.14159;

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

function subtract(a: number, b: number): number {
  return a - b;
}

// 统一导出
export { PI, add, subtract };

// 重命名导出
export { add as sum, subtract as diff };

2.3 默认导出 #

typescript
// calculator.ts

// 默认导出类
export default class Calculator {
  add(a: number, b: number): number {
    return a + b;
  }
}

// 或默认导出函数
export default function greet(name: string): string {
  return `Hello, ${name}!`;
}

// 或默认导出值
export default {
  name: "default config",
  version: "1.0.0"
};

2.4 重导出 #

typescript
// index.ts

// 重导出其他模块
export { add, subtract } from "./math.ts";
export { User } from "./types.ts";

// 重命名后导出
export { add as sum } from "./math.ts";

// 导出所有
export * from "./utils.ts";

// 导出为命名空间
export * as Utils from "./utils.ts";

三、导入(import) #

3.1 命名导入 #

typescript
// main.ts

import { add, subtract, PI } from "./math.ts";

console.log(add(1, 2));      // 3
console.log(subtract(5, 3)); // 2
console.log(PI);             // 3.14159

3.2 重命名导入 #

typescript
import { add as sum, subtract as diff } from "./math.ts";

console.log(sum(1, 2));  // 3
console.log(diff(5, 3)); // 2

3.3 导入默认导出 #

typescript
import Calculator from "./calculator.ts";

const calc = new Calculator();
console.log(calc.add(1, 2)); // 3

3.4 混合导入 #

typescript
import Calculator, { add, subtract } from "./calculator.ts";

const calc = new Calculator();
console.log(add(1, 2));

3.5 导入所有 #

typescript
import * as Math from "./math.ts";

console.log(Math.add(1, 2));      // 3
console.log(Math.subtract(5, 3)); // 2
console.log(Math.PI);             // 3.14159

3.6 仅执行模块 #

typescript
// 执行模块但不导入任何内容
import "./setup.ts";

3.7 动态导入 #

typescript
async function loadModule() {
  const module = await import("./math.ts");
  console.log(module.add(1, 2)); // 3
}

loadModule();

四、模块解析 #

4.1 相对路径 #

typescript
import { add } from "./math.ts";        // 当前目录
import { User } from "../types/user.ts"; // 上级目录
import { config } from "./config/app.ts"; // 子目录

4.2 绝对路径 #

typescript
import { config } from "/Users/user/project/config.ts";

4.3 URL导入 #

typescript
// 从远程URL导入
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";

// 从GitHub导入
import { someModule } from "https://raw.githubusercontent.com/user/repo/main/mod.ts";

// 从CDN导入
import lodash from "https://cdn.skypack.dev/lodash";

4.4 npm包导入 #

typescript
// 使用npm:前缀
import express from "npm:express@4";
import { z } from "npm:zod";

const app = express();

4.5 jsr包导入 #

typescript
// 使用jsr:前缀
import { encodeBase64 } from "jsr:@std/encoding/base64";

const encoded = encodeBase64(new TextEncoder().encode("hello"));
console.log(encoded);

五、模块结构 #

5.1 项目结构 #

text
project/
├── deno.json
├── main.ts
├── src/
│   ├── index.ts
│   ├── utils/
│   │   ├── mod.ts
│   │   ├── math.ts
│   │   └── string.ts
│   └── types/
│       ├── mod.ts
│       └── user.ts
└── tests/
    └── main_test.ts

5.2 模块入口 #

typescript
// src/utils/mod.ts
export { add, subtract } from "./math.ts";
export { capitalize, reverse } from "./string.ts";

// 使用时
import { add, capitalize } from "./src/utils/mod.ts";

5.3 deno.json配置 #

json
{
  "name": "@my-org/my-project",
  "version": "1.0.0",
  "exports": "./src/mod.ts",
  "imports": {
    "@std/http": "jsr:@std/http@^1.0.0",
    "@/utils": "./src/utils/mod.ts"
  }
}

六、循环依赖 #

6.1 循环依赖问题 #

typescript
// a.ts
import { b } from "./b.ts";
export const a = 1;
console.log(b);

// b.ts
import { a } from "./a.ts";
export const b = a + 1; // a可能是undefined

6.2 解决方案 #

typescript
// 方案1:重构代码,消除循环依赖

// 方案2:延迟导入
// a.ts
export const a = 1;
export function getB() {
  const { b } = await import("./b.ts");
  return b;
}

// 方案3:使用函数延迟访问
// b.ts
import { a } from "./a.ts";
export const b = () => a + 1;

七、模块缓存 #

7.1 缓存位置 #

bash
# 查看缓存位置
deno info --location

# macOS/Linux: ~/.cache/deno
# Windows: %LOCALAPPDATA%\deno

7.2 缓存命令 #

bash
# 缓存依赖
deno cache main.ts

# 重新加载所有依赖
deno cache --reload main.ts

# 重新加载特定依赖
deno cache --reload=https://deno.land/std main.ts

7.3 锁定文件 #

bash
# 生成锁定文件
deno cache --lock=deno.lock --lock-write main.ts

# 使用锁定文件
deno cache --lock=deno.lock main.ts

八、最佳实践 #

8.1 使用mod.ts作为入口 #

typescript
// src/mod.ts
export { add, subtract } from "./math.ts";
export { User, Product } from "./types.ts";

8.2 使用类型导出 #

typescript
// types.ts
export type { User, Product };

// 使用时
import type { User } from "./types.ts";

8.3 避免副作用 #

typescript
// 不推荐:模块有副作用
// config.ts
export const config = {};
localStorage.setItem("key", "value"); // 副作用

// 推荐:纯模块
// config.ts
export const config = {};
export function initialize() {
  localStorage.setItem("key", "value");
}

九、总结 #

本章学习了:

  • ES模块的基本概念
  • export导出语法
  • import导入语法
  • 模块解析规则
  • 模块结构组织
  • 循环依赖处理
  • 模块缓存管理
  • 最佳实践

下一章,我们将学习导入导出详解。

最后更新:2026-03-28