Skip to content

第4章:Node.js 模块化开发

4.1 模块化是什么?

模块化是一种将代码拆分为独立、可复用单元的开发方式。在 Node.js 中,每个文件都是一个模块,模块之间通过 require()module.exports 进行通信。

模块化的优势

  • 代码复用:可以将通用功能封装为模块,在多个项目中复用
  • 代码组织:将复杂代码拆分为多个小模块,提高代码可读性和可维护性
  • 避免命名冲突:每个模块有自己的作用域,不会污染全局命名空间
  • 依赖管理:清晰地管理模块之间的依赖关系

4.2 CommonJS 模块化规范

CommonJS 是 Node.js 默认采用的模块化规范,它定义了模块的导入和导出方式。

module.exports 导出模块

在 CommonJS 规范中,使用 module.exports 导出模块:

导出单个值

javascript
// utils.js
const add = (a, b) => a + b;
module.exports = add;

导出多个值

javascript
// utils.js
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;
const multiply = (a, b) => a * b;
const divide = (a, b) => a / b;

module.exports = {
  add,
  subtract,
  multiply,
  divide
};

require() 导入模块

在 CommonJS 规范中,使用 require() 导入模块:

导入核心模块

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

导入自定义模块

javascript
// 导入单个值
const add = require('./utils');

// 导入多个值
const utils = require('./utils');
console.log(utils.add(1, 2)); // 输出: 3
console.log(utils.subtract(5, 3)); // 输出: 2

导入第三方模块

javascript
const lodash = require('lodash');

4.3 ES6 模块化规范

ES6 模块化规范使用 importexport 语法,Node.js 从 v13.2.0 开始支持 ES6 模块。

启用 ES6 模块

要在 Node.js 中使用 ES6 模块,需要:

  1. 将文件扩展名改为 .mjs
  2. 或者在 package.json 中设置 "type": "module"

export 导出模块

导出单个值

javascript
// utils.mjs
export const add = (a, b) => a + b;

导出多个值

javascript
// utils.mjs
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export const multiply = (a, b) => a * b;
export const divide = (a, b) => a / b;

默认导出

javascript
// utils.mjs
const add = (a, b) => a + b;
export default add;

import 导入模块

导入单个值

javascript
// app.mjs
import { add } from './utils.mjs';
console.log(add(1, 2)); // 输出: 3

导入多个值

javascript
// app.mjs
import { add, subtract, multiply, divide } from './utils.mjs';
console.log(add(1, 2)); // 输出: 3
console.log(subtract(5, 3)); // 输出: 2

导入默认值

javascript
// app.mjs
import add from './utils.mjs';
console.log(add(1, 2)); // 输出: 3

导入所有值

javascript
// app.mjs
import * as utils from './utils.mjs';
console.log(utils.add(1, 2)); // 输出: 3
console.log(utils.subtract(5, 3)); // 输出: 2

4.4 自定义模块开发

示例:创建工具模块

步骤1:创建工具模块文件

创建 utils.js 文件:

javascript
// utils.js

/**
 * 计算两个数的和
 * @param {number} a - 第一个数
 * @param {number} b - 第二个数
 * @returns {number}
 */
const add = (a, b) => a + b;

/**
 * 计算两个数的差
 * @param {number} a - 被减数
 * @param {number} b - 减数
 * @returns {number}
 */
const subtract = (a, b) => a - b;

/**
 * 计算两个数的积
 * @param {number} a - 第一个数
 * @param {number} b - 第二个数
 * @returns {number}
 */
const multiply = (a, b) => a * b;

/**
 * 计算两个数的商
 * @param {number} a - 被除数
 * @param {number} b - 除数
 * @returns {number}
 */
const divide = (a, b) => {
  if (b === 0) {
    throw new Error('除数不能为0');
  }
  return a / b;
};

/**
 * 生成指定范围的随机数
 * @param {number} min - 最小值
 * @param {number} max - 最大值
 * @returns {number} 随机数
 */
const random = (min, max) => {
  return Math.floor(Math.random() * (max - min + 1)) + min;
};

// 导出模块
module.exports = {
  add,
  subtract,
  multiply,
  divide,
  random
};

步骤2:使用工具模块

创建 app.js 文件:

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

// 使用工具函数
console.log('1 + 2 =', utils.add(1, 2));
console.log('5 - 3 =', utils.subtract(5, 3));
console.log('2 * 4 =', utils.multiply(2, 4));
console.log('8 / 2 =', utils.divide(8, 2));
console.log('1-10的随机数:', utils.random(1, 10));

步骤3:运行程序

bash
node app.js

输出结果:

1 + 2 = 3
5 - 3 = 2
2 * 4 = 8
8 / 2 = 4
1-10的随机数: 7

4.5 模块查找机制

当使用 require() 导入模块时,Node.js 会按照以下顺序查找模块:

  1. 核心模块:如果是核心模块(如 fs、http),直接返回核心模块
  2. 相对路径或绝对路径:如果路径以 ./..// 开头,按照路径查找文件
  3. node_modules:如果不是核心模块且没有指定路径,会在当前目录的 node_modules 文件夹中查找
  4. 向上查找:如果当前目录的 node_modules 中没有找到,会向上级目录的 node_modules 中查找,直到根目录

模块文件扩展名

当导入模块时,如果没有指定文件扩展名,Node.js 会按照以下顺序尝试添加扩展名:

  1. .js
  2. .json
  3. .node(编译后的 C/C++ 模块)

目录模块

如果导入的是一个目录,Node.js 会在该目录中查找以下文件:

  1. package.json 文件中的 main 字段指定的文件
  2. index.js
  3. index.json
  4. index.node

4.6 实操案例:封装工具模块

案例:创建日期工具模块

步骤1:创建日期工具模块

创建 dateUtils.js 文件:

javascript
// dateUtils.js

/**
 * 格式化日期
 * @param {Date} date - 日期对象
 * @param {string} format - 格式字符串
 * @returns {string} 格式化后的日期字符串
 */
const formatDate = (date, format = 'YYYY-MM-DD') => {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  const hours = String(date.getHours()).padStart(2, '0');
  const minutes = String(date.getMinutes()).padStart(2, '0');
  const seconds = String(date.getSeconds()).padStart(2, '0');

  return format
    .replace('YYYY', year)
    .replace('MM', month)
    .replace('DD', day)
    .replace('HH', hours)
    .replace('mm', minutes)
    .replace('ss', seconds);
};

/**
 * 获取当前日期
 * @param {string} format - 格式字符串
 * @returns {string} 当前日期字符串
 */
const getCurrentDate = (format = 'YYYY-MM-DD') => {
  return formatDate(new Date(), format);
};

/**
 * 计算两个日期之间的天数差
 * @param {Date} date1 - 第一个日期
 * @param {Date} date2 - 第二个日期
 * @returns {number} 天数差
 */
const getDaysDifference = (date1, date2) => {
  const oneDay = 24 * 60 * 60 * 1000;
  const diffTime = Math.abs(date2 - date1);
  return Math.floor(diffTime / oneDay);
};

/**
 * 判断是否是闰年
 * @param {number} year - 年份
 * @returns {boolean} 是否是闰年
 */
const isLeapYear = (year) => {
  return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
};

// 导出模块
module.exports = {
  formatDate,
  getCurrentDate,
  getDaysDifference,
  isLeapYear
};

步骤2:使用日期工具模块

创建 app.js 文件:

javascript
// app.js
const dateUtils = require('./dateUtils');

// 使用日期工具函数
console.log('当前日期:', dateUtils.getCurrentDate());
console.log('当前时间:', dateUtils.getCurrentDate('YYYY-MM-DD HH:mm:ss'));

const date1 = new Date('2026-01-01');
const date2 = new Date('2026-01-10');
console.log('日期差:', dateUtils.getDaysDifference(date1, date2), '天');

console.log('2024年是闰年:', dateUtils.isLeapYear(2024));
console.log('2025年是闰年:', dateUtils.isLeapYear(2025));

步骤3:运行程序

bash
node app.js

输出结果:

当前日期: 2026-04-02
当前时间: 2026-04-02 03:16:00
日期差: 9 天
2024年是闰年: true
2025年是闰年: false

小结

  • 模块化是将代码拆分为独立、可复用单元的开发方式
  • Node.js 默认使用 CommonJS 模块化规范
  • CommonJS 使用 require() 导入模块,使用 module.exports 导出模块
  • ES6 模块化规范使用 importexport 语法
  • 自定义模块可以封装通用功能,提高代码复用性
  • Node.js 有一套完整的模块查找机制,确保模块能够被正确找到

现在,你已经了解了 Node.js 的模块化开发,接下来让我们学习 npm 包管理工具。

© 2026 编程马·菜鸟教程 版权所有