CommonJS

CommonJS 是什么?

CommonJS 是 Node.js 采用的模块规范,主要用于服务器端 JavaScript 开发。它使用同步加载模块的方式,因为服务器端文件都在本地,读取速度快。

核心语法

1. 导出模块(Exports)

CommonJS 提供了两种导出方式:

方式一:module.exports

javascript 复制代码
// math.js
module.exports = {
  add: function(a, b) {
    return a + b;
  },
  subtract: function(a, b) {
    return a - b;
  }
};

// 或者导出单个函数/类
module.exports = function(a, b) {
  return a + b;
};

// 或者导出一个类
module.exports = class Calculator {
  add(a, b) {
    return a + b;
  }
};

方式二:exports(是 module.exports 的引用)

javascript 复制代码
// math.js
exports.add = function(a, b) {
  return a + b;
};

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

⚠️ 注意:exportsmodule.exports 的区别

javascript 复制代码
// ❌ 错误用法 - 这样会切断 exports 和 module.exports 的引用关系
exports = function(a, b) {
  return a + b;
};

// ✅ 正确用法
module.exports = function(a, b) {
  return a + b;
};

// ✅ 或者
exports.add = function(a, b) {
  return a + b;
};

2. 导入模块(Require)

javascript 复制代码
// 导入整个模块
const math = require('./math');
math.add(1, 2);

// 使用解构导入
const { add, subtract } = require('./math');
add(1, 2);

// 导入核心模块
const fs = require('fs');
const path = require('path');

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

详细特性

3. 模块缓存

javascript 复制代码
// counter.js
let count = 0;
module.exports = {
  increment: () => ++count,
  getCount: () => count
};

// main.js
const counter1 = require('./counter');
const counter2 = require('./counter');

counter1.increment(); // 1
console.log(counter2.getCount()); // 1 - 同一个实例!

// 模块只会加载一次,后续 require 返回缓存的模块
console.log(counter1 === counter2); // true

4. 模块查找机制

javascript 复制代码
// 1. 核心模块(优先级最高)
require('fs');  // Node.js 内置模块

// 2. 文件模块
require('./math');        // 当前目录下的 math.js
require('./math.js');     // 明确指定后缀
require('../utils/math'); // 上级目录

// 3. 文件夹模块
require('./myModule');    
// 查找顺序:
// - ./myModule/package.json 的 main 字段
// - ./myModule/index.js
// - ./myModule/index.json
// - ./myModule/index.node

// 4. node_modules 模块
require('express');
// 查找顺序(从当前目录向上递归):
// - ./node_modules/express
// - ../node_modules/express
// - ../../node_modules/express
// - 直到根目录

5. 循环依赖处理

javascript 复制代码
// a.js
console.log('a 开始');
exports.done = false;
const b = require('./b');
console.log('在 a 中,b.done =', b.done);
exports.done = true;
console.log('a 结束');

// b.js
console.log('b 开始');
exports.done = false;
const a = require('./a');
console.log('在 b 中,a.done =', a.done);
exports.done = true;
console.log('b 结束');

// main.js
const a = require('./a');
console.log('main 中,a.done =', a.done);

/* 输出:
a 开始
b 开始
在 b 中,a.done = false  // a 还未执行完,只获取到部分导出
b 结束
在 a 中,b.done = true
a 结束
main 中,a.done = true
*/

6. 模块包装

每个模块在执行前,Node.js 会将代码包装在一个函数中:

javascript 复制代码
(function(exports, require, module, __filename, __dirname) {
  // 你的模块代码
  const math = require('./math');
  module.exports = { /* ... */ };
});

所以在模块中可以直接使用:

  • exports - 导出对象的引用
  • require - 导入函数
  • module - 当前模块对象
  • __filename - 当前文件的绝对路径
  • __dirname - 当前文件所在目录的绝对路径

CommonJS vs ES6 模块

特性 CommonJS ES6 Module
语法 require/module.exports import/export
加载方式 同步加载 异步加载
加载时机 运行时加载 编译时加载
输出 值的拷贝 值的引用
this 指向当前模块 undefined
使用环境 Node.js 浏览器和 Node.js
动态导入 支持(天然) 需要 import()
javascript 复制代码
// CommonJS - 运行时加载,可以动态 require
const moduleName = Math.random() > 0.5 ? './a' : './b';
const module = require(moduleName); // ✅ 可以

if (condition) {
  const math = require('./math'); // ✅ 可以
}

// ES6 - 编译时加载,import 必须在顶层
import math from './math'; // ✅ 必须在顶层

if (condition) {
  import math from './math'; // ❌ 不可以
  import('./math').then(math => {}); // ✅ 需要用动态 import
}

实际应用示例

javascript 复制代码
// logger.js - 工具模块
const chalk = require('chalk'); // 第三方库

class Logger {
  info(msg) {
    console.log(chalk.blue('[INFO]'), msg);
  }
  
  error(msg) {
    console.log(chalk.red('[ERROR]'), msg);
  }
}

module.exports = new Logger(); // 导出单例

// config.js - 配置模块
module.exports = {
  port: process.env.PORT || 3000,
  db: {
    host: 'localhost',
    port: 5432
  }
};

// app.js - 主应用
const express = require('express');
const logger = require('./logger');
const config = require('./config');

const app = express();

app.listen(config.port, () => {
  logger.info(`Server running on port ${config.port}`);
});
相关推荐
꒰ঌ 安卓开发໒꒱13 小时前
RabbitMQ面试全解析:从核心概念到高可用架构
面试·架构·rabbitmq
listhi52013 小时前
利用React Hooks简化状态管理
前端·javascript·react.js
杨筱毅13 小时前
【C++】【常见面试题】最简版带大小和超时限制的LRU缓存实现
c++·面试
一点一木14 小时前
🚀 2025 年 10 月 GitHub 十大热门项目排行榜 🔥
前端·人工智能·github
华仔啊14 小时前
这个Vue3旋转菜单组件让项目颜值提升200%!支持多种主题,拿来即用
前端·javascript·css
非凡ghost14 小时前
Adobe Lightroom安卓版(手机调色软件)绿色版
前端·windows·adobe·智能手机·软件需求
BestAns15 小时前
Postman 平替?这款轻量接口测试工具,本地运行 + 批量回归超实用!
前端
专注前端30年15 小时前
Webpack进阶玩法全解析(性能优化+高级配置)
前端·webpack·性能优化
烛阴15 小时前
Lua世界的基石:变量、作用域与七大数据类型
前端·lua
张拭心15 小时前
“不卷 AI、不碰币、下班不收消息”——Android 知名技术大牛 Jake Wharton 的求职价值观
android·前端·aigc