CommonJS 命名空间
CommonJS 模块由可以是任何类型的 module.exports
对象组成。
当导入 CommonJS 模块时,可以使用 ES 模块默认导入或其对应的语法糖可靠地导入:
import { default as cjs } from 'cjs';
// 下面的导入语句是上面的导入语句中
// `{ default as cjsSugar }` 的 "语法糖"(等价但更甜):
import cjsSugar from 'cjs';
console.log(cjs);
console.log(cjs === cjsSugar);
// 打印:
// <module.exports>
// true
CommonJS 模块的 ECMAScript 模块命名空间表示始终是使用 default
导出键指向 CommonJS module.exports
值的命名空间。
当使用 import * as m from 'cjs'
或动态导入时,可以直接观察到此模块命名空间外来对象:
import * as m from 'cjs';
console.log(m);
console.log(m === await import('cjs'));
// 打印:
// [Module] { default: <module.exports> }
// true
为了更好地兼容 JS 生态系统中的现有用法,Node.js 还尝试确定每个导入的 CommonJS 模块的 CommonJS 命名导出,以使用静态分析过程将它们作为单独的 ES 模块导出提供。
例如,考虑编写的 CommonJS 模块:
// cjs.cjs
exports.name = 'exported';
前面的模块支持 ES 模块中的命名导入:
import { name } from './cjs.cjs';
console.log(name);
// 打印: 'exported'
import cjs from './cjs.cjs';
console.log(cjs);
// 打印: { name: 'exported' }
import * as m from './cjs.cjs';
console.log(m);
// 打印: [Module] { default: { name: 'exported' }, name: 'exported' }
从上一个记录模块命名空间外来对象的示例中可以看出,name
导出是从 module.exports
对象复制出来的,并在导入模块时直接设置在 ES 模块命名空间上。
未检测到这些命名导出的实时绑定更新或添加到 module.exports
的新导出。
命名导出的检测基于通用语法模式,但并不总是正确地检测命名导出。 在这些情况下,使用上述默认导入形式可能是更好的选择。
命名导出检测涵盖了许多常见的导出模式、再导出模式、以及构建工具和转译器输出。 参阅 cjs-module-lexer 以了解实现的确切语义。
CommonJS modules consist of a module.exports
object which can be of any type.
When importing a CommonJS module, it can be reliably imported using the ES module default import or its corresponding sugar syntax:
import { default as cjs } from 'cjs';
// The following import statement is "syntax sugar" (equivalent but sweeter)
// for `{ default as cjsSugar }` in the above import statement:
import cjsSugar from 'cjs';
console.log(cjs);
console.log(cjs === cjsSugar);
// Prints:
// <module.exports>
// true
The ECMAScript Module Namespace representation of a CommonJS module is always
a namespace with a default
export key pointing to the CommonJS
module.exports
value.
This Module Namespace Exotic Object can be directly observed either when using
import * as m from 'cjs'
or a dynamic import:
import * as m from 'cjs';
console.log(m);
console.log(m === await import('cjs'));
// Prints:
// [Module] { default: <module.exports> }
// true
For better compatibility with existing usage in the JS ecosystem, Node.js in addition attempts to determine the CommonJS named exports of every imported CommonJS module to provide them as separate ES module exports using a static analysis process.
For example, consider a CommonJS module written:
// cjs.cjs
exports.name = 'exported';
The preceding module supports named imports in ES modules:
import { name } from './cjs.cjs';
console.log(name);
// Prints: 'exported'
import cjs from './cjs.cjs';
console.log(cjs);
// Prints: { name: 'exported' }
import * as m from './cjs.cjs';
console.log(m);
// Prints: [Module] { default: { name: 'exported' }, name: 'exported' }
As can be seen from the last example of the Module Namespace Exotic Object being
logged, the name
export is copied off of the module.exports
object and set
directly on the ES module namespace when the module is imported.
Live binding updates or new exports added to module.exports
are not detected
for these named exports.
The detection of named exports is based on common syntax patterns but does not always correctly detect named exports. In these cases, using the default import form described above can be a better option.
Named exports detection covers many common export patterns, reexport patterns and build tool and transpiler outputs. See cjs-module-lexer for the exact semantics implemented.