Node.js 模块系统详解:CommonJS 与 ESM 语法对比及混用处理
本文深入解析Node.js中CommonJS与ES6 Module(ESM)两种模块系统的语法差异。详细对比module.exports与export、require与import的使用场景及关键区别,重点说明ESM加载CommonJS模块时的动态绑定限制及解决方案,帮助开发者避免模块引用错误。
Node.js 模块规范演进
社区模块规范概述
1. CommonJS规范
规范实现者:
NodeJS服务端环境
Browserify浏览器环境
2. AMD规范(全称:异步模块定义)
规范实现者:
RequireJS浏览器环境
3. CMD规范(通用模块定义)
规范实现者:
seaJS(服务端与浏览器通用)
官方模块规范
1. ESM规范(即ES6 Module)
各浏览器和服务端环境
目前常用方案包括浏览器端的RequireJS、NodeJS以及ESM
CommonJS 模块语法详解
CommonJS语法分析
module.exports机制
关键点说明
1. module.exports实质上是一个对象,最终模块导出的内容即为该引用指向的对象
module.export.key = value
// eg:
module.export.a = 1;
// 整体管理导出,此时exports与module.exports指向断开,导致exports上的属性被忽略
module.export = {
a:1,
b:1
}
2. exports是module.exports的辅助变量,用于就地导出;两者默认指向同一对象,即 module.exports === exports 为 true
// 像比较长的程序,写完所有之后,再去找到需要导出的变量再移到低端的export.module上逐个添加,是相当麻烦,一般在变量下决定是否导出
//eg
let fA = function () {
}
module.exports.fA = fA
let fB = function () {
}
module.exports.fB = fB
// exports 简洁很多
let fA = function () {
}
module.fA = fA
let fB = function () {
}
module.fB = fB
3. 逐个导出时使用exports,导出整个对象时使用module.exports;不建议混用,若需混用可进行如下处理:
// 在最后导出时将exports对象和module.exports对象合并
exports.a = 1
module.exports = Object.assign({
b : 1
},exports)
require机制
关键点说明
1. 模块区分,了解即可
let f = require('url')
// 核心模块,第三方模块(npm安装),指定模块名即可
let f = require ('modulename')
// 自定义模块,需指定相对或者绝对路径
let f = require('absolutePATH/relativeAPTH')
2. 查找时,若未指定后缀名,会尝试追加.js、.json、.node后缀,此处可简化操作
let f = require('./circle')
// 等同于
let f = require('./circle.js')
ESM (ES6 Module) 语法解析
ESM语法分析
export var/function/class声明
关键点说明
1. 导出值需与内部值存在对应关系,即:
// error
export 1
// error
let m = 1
export m
// correct
export let m = 1
// correct
let m = 1;
export {m}
直接在声明时导出,或使用{}包裹后导出
2. export default用于导出默认值,使用户无需知晓内部变量名即可使用
PS:一个模块仅允许存在一个默认导出
export default 等同 export {add as default}
// 不能接变量声明语句
export default let a = 1
普通导出与默认导用的区别
// 普通导出的导入使用
export {f}
import {f} from 'fmod'
// 默认导出的导入使用,可以无需知道导出模块的内部变量名,任意重命名
export default f
import c from 'fmod'
3. 导出值与模块内部值是动态绑定的
export let foo = 'bar';
// 500毫秒后,其他模块拿到的foo值会变成bar
setTimeout(() => foo = 'baz', 500);
import用法解析
关键点说明
1. 导入非默认变量时需使用{}语法,且变量名必须与导出时一致
// 普通导出的导入使用
export {f}
import {f} from 'fmod'
2. 导入默认变量时省略{},并可对导出变量进行重命名
// 默认导出的导入使用,可以无需知道导出模块的内部变量名,任意重命名
export default f
import c from 'fmod'
3. 可使用*导入整个模块
// circle.js
export let radius = 1
export let area = 2*PI*radius
// main.js
import * as circle from './circle.js'
circle.radius
circle.area
4. 可使用,同时导入默认导出和常规导出
import _,{each, forEach} from 'lodash'
5. 多次import同一模块,仅执行一次
6. import可与require混用,但import在静态分析阶段执行,即先于require加载,需注意导入顺序要求
export { foo, bar } from 'my_module';
// 可以简单理解为,但是合并写法等同于没有在当前模块中导入my_module,即无法使用
import { foo, bar } from 'my_module';
export { foo, bar };
ESM 加载 CommonJS 模块的处理机制
ESM加载CommonJS模块
关键点说明
1. CommonJS模块输出的module.exports对象将被Node.js转换为默认导出
// 导出
export default module.exports
// 导入,类似导入默认
import m from './m'
2. 此时CommonJS模块变量遵循其原有规则,不具备动态绑定特性
module.exports = 123;
setTimeout(() => module.exports = null,500)
//500毫秒后,module.exports仍然是123
3. 由于CommonJS模块在运行时才确定输出的module.exports对象,而ESM在编译时即确定接口,因此导入时不允许使用解构语法
import {readFile} from 'fs'
解决方案
//整体导入
import * as express from 'express'
const app = express.default()
//默认导入,更优
import express from 'express'
const app = express()
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对本站的支持。
在 NestJS 中使用管道验证数据与守卫控制访问权限
2026-11-05 12:43:41
Node.js后端生成高强度随机字符串的方法
2026-11-04 16:29:54
Node.js环境中怎么使用axios或http模块发送GET请求
2026-11-03 21:57:15
Node.js环境下怎么实现JS反混淆自动化?常用库与CLI工具推荐
2026-11-03 21:15:54
解决JS逆向常见报错:缺少DOM环境导致代码运行失败
2026-11-03 20:41:59
