当前位置: 首页 > news >正文

Node.js 模块系统选择-学习 CommonJS 和 ESM

本文摘要:本文记录了在Node.js项目中从CommonJS迁移到ES Modules时遇到的配置加载问题及解决方案。作者在将原有配置系统改造为ESM格式后,遇到了模块导出错误、语法兼容性问题以及WebSocket地址解析错误。通过分析发现,问题的根源在于Node版本过低(v14)不支持import assertions特性,以及CommonJS与ESM模块混用导致的兼容性问题。最终通过统一使用ESM语法、去除不兼容的import参数,成功解决了配置加载问题。文章还详细对比了CommonJS和ESM两种模块系统的使用方式、语法差异和适用场景,为开发者提供了模块系统选择的参考标准。案例表明,在新项目中推荐使用ESM标准,而在旧项目迁移时需特别注意版本兼容性和语法统一性。

目录

一、问题背景

二、遇到的问题

三、分析过程

1. 确认配置加载机制

2. 确认当前 Node.js 模块类型

3. 初步修正:统一为 ESM 写法

4. 运行时报语法错误 “Unexpected token ','”

5. 最终运行成功

CommonJS 和 ESM

1. 什么是模块系统?

2. CommonJS 模块系统

2.1 基本概念

2.2 如何使用 CommonJS

导出模块(把东西放进盒子)

导入模块(从盒子里拿东西)

2.3 CommonJS 特点

3. ESM (ECMAScript Modules) 模块系统

3.1 基本概念

3.2 如何使用 ESM

第一步:告诉 Node.js 使用 ESM

导出模块

导入模块

4. 两种系统的详细对比

4.1 语法对比表

4.2 实际例子对比

5. 常见问题与解决方案

5.1 如何选择使用哪种?

5.2 文件扩展名说明

5.3 特殊变量处理

6. 练习项目

项目:简单的计算器

7. 总结


一、问题背景

在我最近维护的 某平台 中,

Node环境确定、不能混用ESM和CommonJS、修改代码要注意环境版本
项目中原始的配置管理逻辑(main.js + 多个 config_xxx.js)基于 CommonJS(require/module.exports)。

为了适配新版运行环境(Node.js 14+)和平台的自动配置覆盖机制,我将:

  • 原来 main.js 中的 if/else 配置判断逻辑抽取到独立文件;

  • 引入了一个新的模块:config_loader.js

  • 同时,平台层支持通过指定的 config_test.jsconfig_pre.js 等文件覆盖 config.js 的内容。

看似简单的结构优化,却引发了一连串配置加载与运行错误。


二、遇到的问题

在改造完成后运行:

node main.js

我收到了如下长长的错误堆栈👇

SyntaxError: The requested module './config_loader.js' does not provide an export named 'getPlatformPathConfig' 

后续又出现了:在修改代码的时候要注意自己的环境,比如说我这里,就需要注意Node的版本

SyntaxError: Unexpected token ',' 

以及在浏览器端出现的实际问题:

WebSocket connection to '某网址' failed 

而我明明在配置文件中写的是:

signal_ws: 'test.xxx.com/ws' 

却被错误地解析成了 /test-ws/


三、分析过程

1. 确认配置加载机制

平台机制明确指出:

永远加载 config.js,但允许平台层动态覆盖指定环境文件(如 config_test.js)。

因此问题并非出在加载顺序,而是:

  • 我抽取出来的 config_loader.js 没有正确识别覆盖后的文件结构

  • 或者 Node.js 运行时的模块系统混用了 CommonJS 与 ES Module


2. 确认当前 Node.js 模块类型

我检查 package.json

{ "type": "module" } 

说明该项目处于 ES Module 模式
但旧代码还在使用 CommonJS:

const { getPlatformPathConfig } = require('./config_loader'); module.exports = { platform_config: {...} } 

→ 这两种模块机制是 不兼容的,导致 SyntaxError: export not found


3. 初步修正:统一为 ESM 写法

我将所有配置文件改为 ESM:

- module.exports = { xxx_config: {...} } + export default { xxx_config: {...} } 

并调整加载器:

- const path = require('path'); - module.exports = { getxxxPathConfig } + import path from 'path'; + export function getxxxPathConfig() { ... } 

以及 main.js 顶部:

- const { getxxxPathConfig } = require('./config_loader'); + import { getxxxPathConfig } from './config_loader.js'; 

4. 运行时报语法错误 “Unexpected token ','”

出现于:

const { default: configModule } = await import(configPath, { assert: { type: 'javascript' } }) 

这说明 Node.js 版本过低,不支持 import assertions(v17+ 才引入)。
而运行环境是 Node v14.19.3

解决方法:去掉 { assert: ... } 参数,改为:

const configModule = await import(configPath); 

5. 最终运行成功

✅ 配置覆盖机制成功;
✅ WebSocket 地址正确(从 test-ws 修复为 ws);
✅ RabbitMQ、Media、Signal 等模块正常。

CommonJS 和 ESM

1. 什么是模块系统?

简单理解:就像整理房间一样,模块系统帮助我们把代码分成不同的"盒子",每个盒子有特定功能,需要时拿出来用。

为什么需要模块?

  • 避免所有代码写在一个文件里

  • 提高代码可维护性

  • 方便代码复用

  • 团队协作更高效

2. CommonJS 模块系统

2.1 基本概念

CommonJS 是 Node.js 最早使用的模块系统,设计用于服务器端。

2.2 如何使用 CommonJS

导出模块(把东西放进盒子)

math.js

// math.js 文件 - 创建一个数学工具模块// 方法1:导出单个值
module.exports = function add(a, b) {return a + b;
};// 方法2:导出多个值
module.exports = {add: function(a, b) { return a + b; },subtract: function(a, b) { return a - b; },PI: 3.14159
};// 方法3:使用 exports(与上面等效)
exports.add = function(a, b) { return a + b; };
exports.subtract = function(a, b) { return a - b; };
导入模块(从盒子里拿东西)

app.js

// app.js 文件 - 使用数学工具模块// 导入整个模块
const math = require('./math.js');
console.log(math.add(2, 3)); // 5
console.log(math.PI); // 3.14159// 只导入需要的部分(解构赋值)
const { add, PI } = require('./math.js');
console.log(add(5, 3)); // 8

2.3 CommonJS 特点

  • 同步加载:像排队买票,一个接一个

  • 运行时加载:代码执行时才加载模块

  • 值拷贝:导出的是值的副本

3. ESM (ECMAScript Modules) 模块系统

3.1 基本概念

ESM 是 JavaScript 的官方标准模块系统,现代浏览器和 Node.js 都支持。

3.2 如何使用 ESM

第一步:告诉 Node.js 使用 ESM

在 package.json 文件中添加:

{"type": "module"
}
导出模块
// math.js 文件// 命名导出 - 可以导出多个
export function add(a, b) {return a + b;
}export function subtract(a, b) {return a - b;
}export const PI = 3.14159;// 默认导出 - 只能有一个
export default function multiply(a, b) {return a * b;
}
导入模块
// app.js 文件// 导入命名导出
import { add, PI } from './math.js';
console.log(add(2, 3)); // 5// 导入默认导出
import multiply from './math.js';
console.log(multiply(2, 3)); // 6// 导入所有内容
import * as math from './math.js';
console.log(math.add(1, 2)); // 3

4. 两种系统的详细对比

4.1 语法对比表

功能CommonJSESM
导出module.exports = {}export {}
导入const module = require()import module from ''
文件扩展名.js 或 .cjs.js 或 .mjs

4.2 实际例子对比

CommonJS 例子:

// user.js - 导出用户相关功能
const users = ['小明', '小红', '小刚'];function getUser(id) {return users[id] || '用户不存在';
}function addUser(name) {users.push(name);return `用户 ${name} 添加成功`;
}module.exports = {getUser,addUser,userCount: users.length
};
// app.js - 使用用户功能
const userModule = require('./user.js');console.log(userModule.getUser(0)); // 小明
console.log(userModule.addUser('小李')); // 用户 小李 添加成功

ESM 例子:

// user.js - 导出用户相关功能
const users = ['小明', '小红', '小刚'];export function getUser(id) {return users[id] || '用户不存在';
}export function addUser(name) {users.push(name);return `用户 ${name} 添加成功`;
}export const userCount = users.length;
// app.js - 使用用户功能
import { getUser, addUser, userCount } from './user.js';console.log(getUser(0)); // 小明
console.log(addUser('小李')); // 用户 小李 添加成功

5. 常见问题与解决方案

5.1 如何选择使用哪种?

  • 新项目:推荐使用 ESM(现代标准)

  • 旧项目:继续用 CommonJS 或逐步迁移

  • 不确定时:先用 CommonJS,更容易理解

5.2 文件扩展名说明

// .js 文件 - 根据 package.json 决定
// 如果 package.json 有 "type": "module" → 按 ESM 处理
// 如果没有 → 按 CommonJS 处理

// .cjs 文件 - 强制按 CommonJS 处理
// .mjs 文件 - 强制按 ESM 处理

5.3 特殊变量处理

// CommonJS 中有这些变量
console.log(__dirname); // 当前文件所在目录
console.log(__filename); // 当前文件名// ESM 中没有,需要这样获取
import { fileURLToPath } from 'url';
import { dirname } from 'path';const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

6. 练习项目

项目:简单的计算器

分别用 CommonJS 和 ESM 实现:

CommonJS 版本:

// calculator.js
module.exports = {add: (a, b) => a + b,subtract: (a, b) => a - b,multiply: (a, b) => a * b,divide: (a, b) => b !== 0 ? a / b : '不能除以0'
};
// app.js
const calc = require('./calculator.js');
console.log('5 + 3 =', calc.add(5, 3));
console.log('10 / 2 =', calc.divide(10, 2));

ESM 版本:

// calculator.js
export function add(a, b) { return a + b; }
export function subtract(a, b) { return a - b; }
export function multiply(a, b) { return a * b; }
export function divide(a, b) { return b !== 0 ? a / b : '不能除以0';
}
// app.js
import { add, divide } from './calculator.js';
console.log('5 + 3 =', add(5, 3));
console.log('10 / 2 =', divide(10, 2));

7. 总结

特性CommonJSESM
学习难度简单中等
现代性传统现代标准
使用场景Node.js 传统项目新项目、全栈开发
推荐度⭐⭐⭐⭐⭐⭐⭐⭐⭐

结语:

       随着这篇博客接近尾声,我衷心希望我所分享的内容能为你带来一些启发和帮助。学习和理解的过程往往充满挑战,但正是这些挑战让我们不断成长和进步。我在准备这篇文章时,也深刻体会到了学习与分享的乐趣。    

         在此,我要特别感谢每一位阅读到这里的你。是你的关注和支持,给予了我持续写作和分享的动力。我深知,无论我在某个领域有多少见解,都离不开大家的鼓励与指正。因此,如果你在阅读过程中有任何疑问、建议或是发现了文章中的不足之处,都欢迎你慷慨赐教。

        你的每一条反馈都是我前进路上的宝贵财富。同时,我也非常期待能够得到你的点赞、收藏,关注这将是对我莫大的支持和鼓励。当然,我更期待的是能够持续为你带来有价值的内容。

http://www.dtcms.com/a/602004.html

相关文章:

  • 手机网站模板wordpress图片粘贴插件
  • 解决报错net.sf.jsqlparser.statement.select.SelectBody
  • 模板网站如何建站app网站建设多少钱
  • 网站建设主机的功能广告中国
  • HashMap相关问题详解
  • 快站建站教程网站dns解析设置
  • java线程变量ThreadLocal用法篇v1.1
  • 变分自编码器VAE
  • K8s网络之Ingress
  • C语言编程实战:每日刷题 - day 1
  • 免费网站最新域名哈尔滨大型网站建设
  • Xcode编译C语言 | 使用Xcode进行C语言编程的技巧与优化
  • 免费网站设计网站制作方案大全
  • 南昌正规网站公司自己做网站需要啥
  • 项目实战Now in Android:App 模块代码结构分析
  • 企业网站制作 优帮云北京seo产品
  • Oracle 开启归档日志
  • element-ui 用户名密码相关的 input 避免自动填充的方法
  • CSS从0到1
  • 如何架设php网站设计邦
  • 做跨境网站注意事项怎样做外国石雕产品网站
  • 房地产爬虫实战:链家二手房数据抓取与深度分析
  • 核电厂执行器控制系统中的抗辐照MCU选型:为什么需要150krad(Si) TID指标?
  • 360度看C#编程语言
  • 卷积神经网络训练与参数调节全攻略:从数据到模型的实战优化
  • LangGraph 的**核心概念、基本使用步骤和实战示例**
  • 谢岗网站仿做wordpress 图片迁移
  • 网站关键词的分类wordpress 插件 销量
  • 构建面向信创生态的数据中台(八):数据资产运营体系 —— 从治理到价值的信创跃迁
  • 通风管道部件-图形识别超方便