Node.js 有两个系统用于解析说明符和加载模块。

CommonJS模块

默认情况下,Node.js 会将以下内容视为 CommonJS 模块:

  • 带有.cjs扩展名的文件。
  • 当最近的父文件包含值为 的顶级字段时,具有.js扩展名的文件。package.json["type"](https://nodejs.org/api/packages.html#type)"commonjs"
  • 字符串作为参数传入--evalor --print,或通过管道传送到node via STDIN,带有标志--input-type=commonjs
    • 它是完全同步的。
    • 它支持文件夹作为模块
    • 解析说明符时,如果未找到完全匹配,它将尝试添加扩展名(.js、、.json和 finally .node),然后尝试将 文件夹解析为模块
    • 它被视为.jsonJSON 文本文件。
    • .node文件被解释为加载了 process.dlopen().
    • 它将所有缺少.json或.node扩展名的文件视为 JavaScript 文本文件。
    • 它不能用于加载 ECMAScript 模块(尽管可以 从 CommonJS 模块加载 ECMASCript 模块)。当用于加载不是 ECMAScript 模块的 JavaScript 文本文件时,它将作为 CommonJS 模块加载。

ES6 模块

默认情况下,Node.js 会将以下内容视为 CommonJS 模块:

  • 带有.mjs扩展名的文件。
  • 当最近的父文件包含值为 的顶级字段时,具有.js扩展名的文件。package.json["type"](https://nodejs.org/api/packages.html#type)"module"
  • 使用标志作为参数传入--eval或通过管道传递给nodevia的字符串。STDIN--input-type=module
    • 它是异步的。
    • 它负责处理import语句和import()表达式。
    • 它不是猴子可修补的,可以使用loader hooks进行自定义。
    • 它不支持文件夹作为模块, ‘./startup/index.js’必须完全指定目录索引(例如)。
    • 它不进行扩展搜索。当说明符是相对或绝对文件 URL 时,必须提供文件扩展名。
    • 它可以加载 JSON 模块,但需要导入断言。
    • 它只接受JavaScript 文本文件的.js、.mjs和.cjs扩展名。
    • 它可用于加载 JavaScript CommonJS 模块。此类模块通过传递cjs-module-lexer以尝试识别命名导出,如果它们可以通过静态分析确定,则它们是可用的。导入的 CommonJS 模块将其 URL 转换为绝对路径,然后通过 CommonJS 模块加载器加载。

CommonJS 模块加载 ES6 模块

CommonJS 的require()命令不能加载 ES6 模块,会报错,只能使用import()这个方法加载。

  1. await import('./my-app.mjs');

ES6 模块加载 CommonJS 模块

ES6 模块的import命令可以加载 CommonJS 模块,但是只能整体加载,不能只加载单一的输出项。

  1. // 正确
  2. import packageMain from 'commonjs-package';
  3. // 报错
  4. import { method } from 'commonjs-package';

这是因为 ES6 模块需要支持静态代码分析,而 CommonJS 模块的输出接口是module.exports,是一个对象,无法被静态分析,所以只能整体加载。

加载单一的输出项,可以写成下面这样。

  1. import packageMain from 'commonjs-package';
  2. const { method } = packageMain;

同时支持两种格式的模块

模块封装

  • 原始模块是 ES6 格式:要给出一个整体输出接口,比如export default obj,使得 CommonJS 可以用import()进行加载。
  • 原始模块是 CommonJS 格式:那么可以加一个包装层。
    1. import cjsModule from '../index.js';
    2. export const foo = cjsModule.foo;
    上面代码先整体输入 CommonJS 模块,然后再根据需要输出具名接口。

    入口文件暴露

    可以把这个文件的后缀名改为.mjs,或者将它放在一个子目录,再在这个子目录里面放一个单独的package.json文件,指明{ type: "module" }

还可以在package.json文件指定export字段,分别指定两种规范的入口文件

  1. "exports":{
  2. "require": "./index.js"
  3. "import": "./esm/wrapper.js"
  4. }

参考链接