Skip to content
On this page

Node.js 模块系统

Node.js使用模块化的方式来组织代码,使得代码更易于维护和重用。本章详细介绍Node.js的模块系统。

CommonJS模块系统

Node.js使用CommonJS规范来实现模块化,这是Node.js原生的模块系统。

模块导出

导出单个函数或变量

javascript
// math.js
function add(a, b) {
  return a + b;
}

function multiply(a, b) {
  return a * b;
}

// 导出单个函数
module.exports = add;

// 或者导出多个函数
module.exports = {
  add,
  multiply
};

导出为属性

javascript
// math.js
function add(a, b) {
  return a + b;
}

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

// 分别导出
module.exports.add = add;
module.exports.subtract = subtract;

// 或使用ES6简写
module.exports = { add, subtract };

导出构造函数或类

javascript
// user.js
class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  getInfo() {
    return `${this.name} (${this.email})`;
  }
}

module.exports = User;

模块导入

javascript
// app.js
// 导入整个模块
const math = require('./math');
console.log(math.add(2, 3)); // 5

// 解构导入
const { add, multiply } = require('./math');
console.log(add(2, 3)); // 5

// 导入类
const User = require('./user');
const user = new User('张三', 'zhang@example.com');
console.log(user.getInfo()); // 张三 (zhang@example.com)

核心模块

Node.js提供了一系列内置的核心模块:

javascript
// 文件系统模块
const fs = require('fs');

// HTTP模块
const http = require('http');

// 路径模块
const path = require('path');

// 操作系统模块
const os = require('os');

// 加密模块
const crypto = require('crypto');

// URL模块
const url = require('url');

常用核心模块示例

文件系统操作

javascript
const fs = require('fs');
const path = require('path');

// 读取文件(异步)
fs.readFile('./data.txt', 'utf8', (err, data) => {
  if (err) {
    console.error('读取文件失败:', err);
    return;
  }
  console.log('文件内容:', data);
});

// 读取文件(同步)
try {
  const data = fs.readFileSync('./data.txt', 'utf8');
  console.log('文件内容:', data);
} catch (err) {
  console.error('读取文件失败:', err);
}

// 写入文件
fs.writeFile('./output.txt', 'Hello, Node.js!', (err) => {
  if (err) {
    console.error('写入文件失败:', err);
    return;
  }
  console.log('文件写入成功');
});

路径处理

javascript
const path = require('path');

// 路径拼接
const filePath = path.join(__dirname, 'data', 'file.txt');
console.log(filePath); // /current/directory/data/file.txt

// 获取路径信息
const parsedPath = path.parse('/home/user/documents/file.txt');
console.log(parsedPath);
// {
//   root: '/',
//   dir: '/home/user/documents',
//   base: 'file.txt',
//   ext: '.txt',
//   name: 'file'
// }

// 获取绝对路径
const absolutePath = path.resolve('data', 'file.txt');
console.log(absolutePath); // /current/directory/data/file.txt

第三方模块

npm模块安装和使用

bash
# 安装生产依赖
npm install express

# 安装开发依赖
npm install --save-dev nodemon

# 安装特定版本
npm install lodash@4.17.20

# 安装全局模块
npm install -g pm2

使用第三方模块

javascript
// 安装express后
const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Hello, Express!');
});

app.listen(3000, () => {
  console.log('服务器运行在端口3000');
});

ES6模块系统

Node.js也支持ES6模块语法(需要在package.json中设置"type": "module"或使用.mjs扩展名):

ES6模块导出

javascript
// math.mjs 或在package.json设置"type": "module"
// 默认导出
export default function add(a, b) {
  return a + b;
}

// 命名导出
export function multiply(a, b) {
  return a * b;
}

export const PI = 3.14159;

// 或者在末尾导出
function subtract(a, b) {
  return a - b;
}

export { subtract };

ES6模块导入

javascript
// app.mjs
import add, { multiply, PI } from './math.mjs';
import { subtract } from './math.mjs';

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

// 导入所有内容
import * as math from './math.mjs';
console.log(math.add(2, 3)); // 5

模块缓存机制

Node.js会缓存模块,确保模块只执行一次:

javascript
// counter.js
let count = 0;

function increment() {
  return ++count;
}

console.log('模块执行');
module.exports = { increment };
javascript
// app.js
const counter1 = require('./counter');
const counter2 = require('./counter');

console.log(counter1.increment()); // 1
console.log(counter2.increment()); // 2 (共享同一个模块实例)

// "模块执行" 只会输出一次,因为模块被缓存了

模块路径解析

Node.js按照以下顺序解析模块路径:

  1. 核心模块
  2. 相对路径(以./或../开头)
  3. 绝对路径(以/开头)
  4. node_modules中的模块
javascript
// 相对路径导入
const myModule = require('./myModule');
const siblingModule = require('../sibling/Module');

// node_modules中的模块
const express = require('express');
const lodash = require('lodash');

// 模块路径解析示例
// require('myModule') 会按以下顺序查找:
// 1. ./node_modules/myModule
// 2. ../node_modules/myModule
// 3. ../../node_modules/myModule
// 直到根目录

package.json和package-lock.json

package.json

json
{
  "name": "my-node-app",
  "version": "1.0.0",
  "description": "A sample Node.js application",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js",
    "test": "jest"
  },
  "dependencies": {
    "express": "^4.18.0",
    "lodash": "^4.17.21"
  },
  "devDependencies": {
    "nodemon": "^2.0.0",
    "jest": "^28.0.0"
  },
  "engines": {
    "node": ">=14.0.0"
  }
}

模块最佳实践

javascript
// 1. 使用解构导入减少重复
const { readFile, writeFile } = require('fs');

// 2. 按类型分组导入
// 核心模块
const fs = require('fs');
const path = require('path');

// 第三方模块
const express = require('express');
const _ = require('lodash');

// 本地模块
const config = require('./config');
const utils = require('./utils');

// 3. 使用index.js作为目录入口
// ./lib/index.js
module.exports = {
  validator: require('./validator'),
  formatter: require('./formatter'),
  parser: require('./parser')
};

// 可以这样导入
const lib = require('./lib'); // 自动加载index.js

自定义模块示例

创建一个实用工具模块:

javascript
// utils.js
const fs = require('fs');
const path = require('path');

// 文件操作工具
const fileUtils = {
  // 检查文件是否存在
  exists: (filePath) => {
    return fs.existsSync(filePath);
  },

  // 读取JSON文件
  readJSON: (filePath) => {
    try {
      const data = fs.readFileSync(filePath, 'utf8');
      return JSON.parse(data);
    } catch (err) {
      throw new Error(`读取JSON文件失败: ${err.message}`);
    }
  },

  // 写入JSON文件
  writeJSON: (filePath, data) => {
    try {
      const json = JSON.stringify(data, null, 2);
      fs.writeFileSync(filePath, json, 'utf8');
    } catch (err) {
      throw new Error(`写入JSON文件失败: ${err.message}`);
    }
  }
};

// 数据验证工具
const validationUtils = {
  isEmail: (email) => {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(email);
  },

  isURL: (url) => {
    try {
      new URL(url);
      return true;
    } catch {
      return false;
    }
  }
};

module.exports = {
  fileUtils,
  validationUtils
};

模块系统是Node.js的重要特性,合理使用模块可以让代码更清晰、更易于维护。