Node.js中的模块
Node.js 作为服务端 JavaScript 运行环境,从一开始就支持模块化开发。它经历了从 CommonJS 到 ES 模块的演进,为服务端应用提供了强大的模块管理能力。
Node.js 模块系统演进
发展历程
1. CommonJS 时代 (2009-2017)
- 原生 CommonJS 支持:Node.js 从创建之初就支持 require/exports
- 同步加载:适合服务端环境的同步模块加载
- npm 生态:围绕 CommonJS 建立的庞大包管理生态系统
2. ES 模块支持 (2017-至今)
- 实验性支持 (Node.js 8.5+):通过 flag 启用 ES 模块
- 稳定支持 (Node.js 12+):正式支持 ES 模块
- 双模块系统:CommonJS 和 ES 模块并存
当前状态 (Node.js 18+)
// Node.js 现在同时支持两种模块系统
// CommonJS (默认)
const fs = require('fs');
const { readFile } = require('fs/promises');
// ES 模块 (需要配置)
import fs from 'fs';
import { readFile } from 'fs/promises';
CommonJS 模块系统
基础语法
module.exports 和 exports
// math.js - 多种导出方式
// 1. 单个函数导出
module.exports = function add(a, b) {
return a + b;
};
// 2. 多个导出
exports.add = (a, b) => a + b;
exports.subtract = (a, b) => a - b;
exports.PI = 3.14159;
// 3. 对象导出
module.exports = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
constants: {
PI: 3.14159,
E: 2.71828
}
};
// 4. 类导出
class Calculator {
add(a, b) { return a + b; }
subtract(a, b) { return a - b; }
}
module.exports = Calculator;
require 导入
// 导入方式
// 1. 导入整个模块
const math = require('./math');
console.log(math.add(2, 3)); // 5
// 2. 解构导入
const { add, subtract } = require('./math');
console.log(add(2, 3)); // 5
// 3. 导入类
const Calculator = require('./calculator');
const calc = new Calculator();
// 4. 导入 Node.js 内置模块
const fs = require('fs');
const path = require('path');
const { createServer } = require('http');
模块解析机制
模块查找顺序
// require('./user') 的查找顺序:
// 1. ./user
// 2. ./user.js
// 3. ./user.json
// 4. ./user.node
// 5. ./user/package.json (main字段)
// 6. ./user/index.js
// 7. ./user/index.json
// 8. ./user/index.node
// require('lodash') 的查找顺序:
// 1. 当前目录 node_modules/lodash
// 2. 父目录 ../node_modules/lodash
// 3. 继续向上查找直到根目录
// 4. 全局 node_modules 目录
// 5. Node.js 内置模块
package.json 配置
{
"name": "my-package",
"version": "1.0.0",
"main": "lib/index.js", // CommonJS 入口
"exports": { // 现代模块解析
".": {
"import": "./esm/index.js", // ES 模块入口
"require": "./cjs/index.js" // CommonJS 入口
},
"./utils": {
"import": "./esm/utils.js",
"require": "./cjs/utils.js"
}
},
"type": "commonjs" // 默认模块类型
}
模块缓存机制
// 模块缓存示例
// counter.js
let count = 0;
exports.increment = () => ++count;
exports.getCount = () => count;
// app.js
const counter1 = require('./counter');
const counter2 = require('./counter'); // 从缓存加载
counter1.increment();
console.log(counter2.getCount()); // 1 - 共享状态
// 手动清除缓存
delete require.cache[require.resolve('./counter')];
const counter3 = require('./counter');
console.log(counter3.getCount()); // 0 - 重新初始化
ES 模块系统
配置和启用
package.json 配置
{
"type": "module", // 启用 ES 模块模式
"exports": {
".": {
"import": "./index.js",
"require": "./index.cjs"
}
}
}
文件扩展名规则
// 在 "type": "module" 项目中:
// .js - ES 模块
// .mjs - ES 模块 (明确)
// .cjs - CommonJS 模块
// 在 "type": "commonjs" 项目中 (默认):
// .js - CommonJS 模块
// .mjs - ES 模块
// .cjs - CommonJS 模块
ES 模块语法
导入导出
// math.mjs
export function add(a, b) {
return a + b;
}
export const PI = 3.14159;
export default class Calculator {
multiply(a, b) {
return a * b;
}
}
// app.mjs
import Calculator, { add, PI } from './math.mjs';
import * as math from './math.mjs';
const calc = new Calculator();
console.log(add(2, 3));
console.log(calc.multiply(4, 5));
动态导入
// 条件导入
async function loadModule(env) {
if (env === 'production') {
const prod = await import('./config/production.js');
return prod.config;
} else {
const dev = await import('./config/development.js');
return dev.config;
}
}
// 懒加载
async function processLargeData(data) {
// 只在需要时加载重型模块
const { heavyProcessor } = await import('./heavy-processor.js');
return heavyProcessor.process(data);
}
内置模块导入
// ES 模块方式导入内置模块
import { readFile, writeFile } from 'fs/promises';
import { join, dirname } from 'path';
import { createServer } from 'http';
import { fileURLToPath } from 'url';
// 获取当前文件路径 (ES 模块中没有 __dirname)
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// 使用 import() 动态导入 CommonJS 模块
const config = await import('./config.json', {
assert: { type: 'json' }
});
双模块系统互操作
CommonJS 导入 ES 模块
// CommonJS 文件 (.cjs 或在 type: "commonjs" 项目中)
async function loadESModule() {
// 只能通过动态导入
const { default: Calculator, add } = await import('./math.mjs');
const calc = new Calculator();
return calc.multiply(add(2, 3), 4);
}
// 不能使用 require() 导入 ES 模块
// const math = require('./math.mjs'); // 错误!
ES 模块导入 CommonJS
// ES 模块文件 (.mjs 或在 type: "module" 项目中)
// 1. 默认导入
import fs from 'fs'; // 整个 exports 对象
import express from 'express'; // CommonJS 库
// 2. 命名导入 (如果支持)
import { readFile } from 'fs/promises';
// 3. 动态导入
const lodash = await import('lodash');
const _ = lodash.default;
// 4. 创建 require 函数 (兼容方案)
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const oldModule = require('./old-commonjs-module');
Node.js 特有特性
文件系统操作
// ES 模块中的文件操作
import { readFile, writeFile } from 'fs/promises';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
async function processFile() {
const configPath = join(__dirname, 'config.json');
const data = await readFile(configPath, 'utf8');
const config = JSON.parse(data);
// 处理配置...
await writeFile(configPath, JSON.stringify(config, null, 2));
}
环境变量和配置
// 环境配置模块
// config.js
const config = {
port: process.env.PORT || 3000,
database: {
host: process.env.DB_HOST || 'localhost',
port: process.env.DB_PORT || 5432,
name: process.env.DB_NAME || 'myapp'
},
jwt: {
secret: process.env.JWT_SECRET || 'default-secret',
expiresIn: process.env.JWT_EXPIRES || '1h'
}
};
export default config;
// app.js
import config from './config.js';
import { createServer } from 'http';
const server = createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'Server running', config }));
});
server.listen(config.port, () => {
console.log(`Server running on port ${config.port}`);
});
进程管理和集群
// cluster.js - 多进程管理
import cluster from 'cluster';
import { cpus } from 'os';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const numCPUs = cpus().length;
if (cluster.isPrimary) {
console.log(`Primary ${process.pid} is running`);
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died`);
cluster.fork(); // 重启死掉的worker
});
} else {
// Worker 进程
const { startApp } = await import('./app.js');
startApp();
console.log(`Worker ${process.pid} started`);
}
性能优化策略
模块预加载和缓存
// 模块预加载器
class ModulePreloader {
constructor() {
this.cache = new Map();
}
async preload(moduleUrls) {
const promises = moduleUrls.map(async url => {
try {
const module = await import(url);
this.cache.set(url, module);
return { url, success: true };
} catch (error) {
console.warn(`Failed to preload ${url}:`, error);
return { url, success: false, error };
}
});
return Promise.all(promises);
}
get(url) {
return this.cache.get(url);
}
}
// 使用示例
const preloader = new ModulePreloader();
await preloader.preload([
'./heavy-computation.js',
'./database-utils.js',
'./email-service.js'
]);
代码分割和懒加载
// 路由懒加载
class Router {
constructor() {
this.routes = new Map();
}
register(path, moduleUrl) {
this.routes.set(path, moduleUrl);
}
async handle(request) {
const url = new URL(request.url, 'http://localhost');
const moduleUrl = this.routes.get(url.pathname);
if (!moduleUrl) {
return { status: 404, body: 'Not Found' };
}
try {
const module = await import(moduleUrl);
return await module.default(request);
} catch (error) {
console.error('Route handler error:', error);
return { status: 500, body: 'Internal Server Error' };
}
}
}
// 使用示例
const router = new Router();
router.register('/api/users', './handlers/users.js');
router.register('/api/posts', './handlers/posts.js');
router.register('/api/auth', './handlers/auth.js');
内存管理
// 内存高效的模块加载
import { Worker, isMainThread, parentPort } from 'worker_threads';
class MemoryEfficientProcessor {
constructor() {
this.workers = [];
this.taskQueue = [];
}
async processInWorker(data, moduleUrl) {
return new Promise((resolve, reject) => {
const worker = new Worker(`
import { parentPort } from 'worker_threads';
import processor from '${moduleUrl}';
parentPort.on('message', async (data) => {
try {
const result = await processor.process(data);
parentPort.postMessage({ success: true, result });
} catch (error) {
parentPort.postMessage({ success: false, error: error.message });
}
process.exit(0); // 处理完后立即退出,释放内存
});
`, { eval: true });
worker.postMessage(data);
worker.on('message', ({ success, result, error }) => {
if (success) {
resolve(result);
} else {
reject(new Error(error));
}
});
worker.on('error', reject);
});
}
}
调试和开发工具
调试配置
// debug.js - 开发环境调试工具
import { inspect } from 'util';
export class Debug {
constructor(namespace) {
this.namespace = namespace;
this.enabled = process.env.DEBUG?.includes(namespace) || false;
}
log(...args) {
if (!this.enabled) return;
const timestamp = new Date().toISOString();
const prefix = `[${timestamp}] ${this.namespace}:`;
console.log(prefix, ...args.map(arg =>
typeof arg === 'object' ? inspect(arg, { colors: true, depth: 3 }) : arg
));
}
error(...args) {
const timestamp = new Date().toISOString();
const prefix = `[${timestamp}] ${this.namespace}:ERROR`;
console.error(prefix, ...args);
}
}
// 使用示例
const debug = new Debug('app:server');
debug.log('Server starting...', { port: 3000, env: process.env.NODE_ENV });
性能监控
// performance.js
import { performance, PerformanceObserver } from 'perf_hooks';
export class PerformanceMonitor {
constructor() {
this.metrics = new Map();
this.setupObserver();
}
setupObserver() {
const obs = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
this.recordMetric(entry.name, entry.duration);
}
});
obs.observe({ entryTypes: ['measure'] });
}
start(name) {
performance.mark(`${name}-start`);
}
end(name) {
performance.mark(`${name}-end`);
performance.measure(name, `${name}-start`, `${name}-end`);
}
recordMetric(name, value) {
if (!this.metrics.has(name)) {
this.metrics.set(name, []);
}
this.metrics.get(name).push(value);
}
getAverageTime(name) {
const times = this.metrics.get(name) || [];
return times.length > 0 ? times.reduce((a, b) => a + b) / times.length : 0;
}
}
// 使用示例
const monitor = new PerformanceMonitor();
async function loadHeavyModule() {
monitor.start('module-load');
const module = await import('./heavy-module.js');
monitor.end('module-load');
return module;
}
打包和部署
生产环境优化
// build.js - 构建脚本
import { build } from 'esbuild';
import { readdir, stat } from 'fs/promises';
import { join } from 'path';
async function findEntryPoints(dir) {
const entries = [];
const files = await readdir(dir);
for (const file of files) {
const filePath = join(dir, file);
const stats = await stat(filePath);
if (stats.isFile() && file.endsWith('.js')) {
entries.push(filePath);
}
}
return entries;
}
async function buildForProduction() {
const entryPoints = await findEntryPoints('./src');
await build({
entryPoints,
bundle: true,
minify: true,
sourcemap: false,
target: 'node18',
platform: 'node',
format: 'esm',
outdir: './dist',
external: ['sharp', 'canvas'] // 不打包的原生依赖
});
console.log('Build completed successfully!');
}
buildForProduction().catch(console.error);
Docker 配置
# Dockerfile
FROM node:18-alpine
WORKDIR /app
# 复制 package 文件
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production
# 复制源码
COPY dist/ ./dist/
# 设置环境变量
ENV NODE_ENV=production
# 启动应用
CMD ["node", "dist/index.js"]
最佳实践总结
模块组织
- 清晰的模块边界 - 每个模块职责单一
- 一致的导入路径 - 使用绝对路径或路径别名
- 避免循环依赖 - 通过依赖注入或重构解决
性能优化
- 合理使用缓存 - 利用模块缓存机制
- 懒加载非关键模块 - 提高启动速度
- Worker 隔离 - 防止内存泄漏
生产环境
- 环境配置分离 - 不同环境使用不同配置
- 错误处理完善 - 模块加载失败的降级方案
- 监控和日志 - 跟踪模块性能和错误
Node.js 的模块系统为服务端 JavaScript 开发提供了强大的能力,合理使用这些特性可以构建出高性能、可维护的后端应用。
下一章: Deno的模块系统 →