Node.js 的模块系统为开发者提供了强大的工具,用于组织代码、管理依赖,并支持不同的模块标准。这包括 CommonJS 模块、ECMAScript 模块(ESM)、node:module
模块、Packages 和 TypeScript 集成。本文将详细介绍这些模块系统的属性、方法、使用方法及相关内容。
1. CommonJS 模块
1.1 模块导入与导出
CommonJS 是 Node.js 默认的模块系统。每个文件都是一个模块,可以导出函数、对象或原始值。模块通过 module.exports
导出,并通过 require()
导入。
导出
// myModule.js
const myFunction = () => {
console.log('Hello from myFunction');
};
module.exports = myFunction;
导入
// app.js
const myFunction = require('./myModule');
myFunction(); // 输出: "Hello from myFunction"
1.2 require
函数
require()
是 CommonJS 中用于加载模块的函数。它会同步读取并执行指定的模块文件,并返回 module.exports
的内容。
使用
const fs = require('fs'); // 导入核心模块
const myModule = require('./myModule'); // 导入自定义模块
缓存机制
模块只会在首次加载时执行一次,之后的加载会直接从缓存中返回结果。这可以避免重复执行相同模块。
// 第一次加载
const module1 = require('./myModule');
// 再次加载,直接从缓存中返回
const module2 = require('./myModule');
console.log(module1 === module2); // 输出: true
1.3 module
对象
每个模块都拥有一个 module
对象,代表当前模块。
module.exports
: 模块的导出对象,其他模块通过require
导入的内容即为module.exports
的值。
示例
// myModule.js
console.log(module); // 输出当前模块的信息
1.4 exports
对象
exports
是 module.exports
的快捷方式,两者指向同一个对象。可以使用 exports
导出多个属性。
示例
// myModule.js
exports.hello = () => {
console.log('Hello');
};
exports.goodbye = () => {
console.log('Goodbye');
};
2. ECMAScript 模块(ESM)
Node.js 也支持 ECMAScript 模块(ESM),这是一种现代的模块系统,最初在浏览器中引入,并在 ECMAScript 规范中标准化。ESM 的特性包括静态解析、异步加载等。
2.1 import
语句
import
语句用于从其他模块中导入绑定。
导入默认导出
import myFunction from './myModule.js';
myFunction();
导入命名导出
import { myFunction, myVariable } from './myModule.js';
动态导入
ESM 允许动态导入模块,返回一个 Promise
,可用于按需加载模块。
const module = await import('./myModule.js');
module.myFunction();
2.2 export
语句
export
语句用于从模块中导出绑定,可以是函数、变量、类等。
导出默认值
export default function() {
console.log('Hello from default export');
}
导出多个值
export const myVariable = 42;
export function myFunction() {
console.log('Hello from myFunction');
}
2.3 package.json
配置
为了使用 ESM,package.json
文件中需要设置 "type": "module"
。这会将 .js
文件视为 ESM 模块。
{
"type": "module"
}
3. node:module
模块
node:module
模块提供了与 Node.js 模块系统交互的功能,例如动态加载模块、创建自定义的 require
函数等。
3.1 require()
与 import()
require()
用于 CommonJS 模块,而 import()
用于 ESM。两者之间不能混用,但 import()
可以用于动态导入 CommonJS 模块。
3.2 createRequire()
createRequire()
创建一个新的 require
函数,用于在 ESM 中导入 CommonJS 模块。
示例
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const fs = require('fs');
3.3 createRequireFromPath()
createRequireFromPath()
已被废弃。推荐使用 createRequire()
代替。
4. Packages
Node.js 的包管理系统允许使用 npm
发布、安装和管理包。package.json
是包的配置文件,包含了包的元数据和依赖信息。
4.1 package.json
中的模块类型
package.json
中的 "type"
字段决定了文件的模块类型:
"type": "commonjs"
: 将.js
文件视为 CommonJS 模块。"type": "module"
: 将.js
文件视为 ESM 模块。
4.2 包的解析和加载
Node.js 根据 require()
或 import
语句解析和加载模块。加载顺序如下:
- 核心模块(如
fs
、http
等) - 本地模块(绝对路径或相对路径)
node_modules
目录中的包
4.3 exports
字段
exports
字段用于定义包的入口点,支持定义不同的导出路径和模块类型。
示例
{
"exports": {
".": "./main.js",
"./feature": "./feature.js"
}
}
5. TypeScript 和 Node.js
TypeScript 提供了静态类型检查和现代 JavaScript 特性,并与 Node.js 无缝集成。
5.1 TypeScript 配置
通过 tsconfig.json
文件配置 TypeScript 编译选项。
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "./dist"
}
}
5.2 TypeScript 中的模块
TypeScript 支持 CommonJS 和 ESM 模块系统,可以根据需要选择配置。
示例
// 使用 CommonJS 模块
import fs = require('fs');
// 使用 ESM 模块
import { readFile } from 'fs/promises';
5.3 TypeScript 和 ESM
要在 TypeScript 中使用 ESM,必须在 tsconfig.json
中设置 module
为 ESNext
或 ES6
。
{
"compilerOptions": {
"module": "ESNext",
"target": "ES6"
}
}
6. 示例代码
CommonJS 示例
// myModule.js
exports.hello = () => {
console.log('Hello');
};
// app.js
const myModule = require('./myModule');
myModule.hello();
ESM 示例
// myModule.js
export function hello() {
console.log('Hello');
}
// app.js
import { hello } from './myModule.js';
hello();
TypeScript 示例
// myModule.ts
export const greet = (name: string): string => {
return `Hello, ${name}`;
};
// app.ts
import { greet } from './myModule';
console.log(greet('World'));
7. 总结
Node.js 提供了多种模块系统,支持 CommonJS、ECMAScript 模块、TypeScript 等。开发者可以根据项目需求选择适合的模块系统,并通过配置 package.json
文件和 TypeScript 配置文件,优化项目的模块管理和构建流程。随着 ESM 的普及,未来的 Node.js 项目将更趋向于使用 ESM 进行模块管理。