Skip to content

Node.js 中使用 package.json 文件中的以下字段:

  1. name - 在包中使用命名导入时相关。包管理器也将其用作包的名称。
  2. main - 如果未指定导出,以及在引入导出之前的 Node.js 版本中,加载包时的默认模块。
  3. packageManager - 为包做贡献时推荐的包管理器。由 Corepack 垫片利用。
  4. type - 包类型决定是否将 .js 文件加载为 CommonJSES 模块。
  5. exports - 打包导出和有条件导出。如果存在,则限制可以从包内加载哪些子模块。
  6. imports - 包导入,供包本身内的模块使用。

1.CommonJS

当作为初始输入传递给node时,或者被 import 语句或 import() 表达式引用时,Node.js 会将以下内容视为 CommonJS

  1. Files with a .cjs extension.

扩展名为 .cjs 文件。

  1. Files with a .js extension when the nearest parent package.json file contains a top-level field "type" with a value of "commonjs". 当最近的父 package.json 文件包含值为 commonjs 的顶级字段 type 时,具有 .js 扩展名的文件。

  2. Strings passed in as an argument to --eval or --print, or piped to node via STDIN, with the flag --input-type=commonjs. 字符串作为参数传入 --eval--print ,或通过 STDIN 通过管道传输到 node ,并带有标志 --input-type=commonjs

.cjs 结尾的文件始终作为 CommonJS 加载,无论最近的父 package.json 是什么。

2.ESM

当作为初始输入传递给 node 时,或者被 import 语句或 import() 表达式引用时,Node.js 会将以下内容视为 ES 模块:

  1. Files with an .mjs extension.

扩展名为 .mjs 文件。

  1. Files with a .js extension when the nearest parent package.json file contains a top-level "type" field with a value of "module".

当最近的父 package.json 文件包含值为 module 的顶级 type 字段时,具有 .js 扩展名的文件。

  1. Strings passed in as an argument to --eval, or piped to node via STDIN, with the flag --input-type=module.

字符串作为参数传入 --eval ,或通过 STDIN 通过管道传输到 node ,并带有标志 --input-type=module

  1. When using --experimental-detect-module, code containing syntax only successfully parsed as ES modules, such as import or export statements or import.meta, having no explicit marker of how it should be interpreted. Explicit markers are .mjs or .cjs extensions, package.json "type" fields with either "module" or "commonjs" values, or --input-type or --experimental-default-type flags. Dynamic import() expressions are supported in either CommonJS or ES modules and would not cause a file to be treated as an ES module.

当使用 --experimental-detect-module 时,包含仅成功解析为 ES 模块的语法的代码,例如 importexport 语句或 import.meta ,没有明确标记应如何解释它。

显式标记是 .mjs.cjs 扩展名、带有 modulecommonjs 值的 package.json type字段,或者 --input-type--experimental-default-type 标志。 CommonJSES 模块都支持动态 import() 表达式,并且不会导致文件被视为 ES 模块。

.mjs 结尾的文件始终作为 ES 模块加载,无论最近的父 package.json 是什么。

import能够导入CommonJS模块 但require不能导入ES模块。

CommonJS模块中,可以使用import懒加载加载esm模块。

3.entry.cjs

package.json 中的 main 字段通常只能定义一个入口。

这样的话,往往只能使用 require('module'),而是用 require('module/lib/module.js') 则会失败。

在这种场景下,可以在 package.json 中定义 exports 字段。

json
{
  "exports": {
    ".": "./index.js",
    "./lib/*": "./lib/*.js"
  }
}
js
const message = require('commonjs-a')
console.log(message)

const libMessage = require('commonjs-a/lib')
console.log(libMessage)

4.entry.mjs

在包的 package.json 文件中,两个字段可以定义包的入口点: mainexports

这两个字段都适用于 ES 模块和 CommonJS 模块入口点。

所有版本的 Node.js 都支持 main 字段,但其功能有限:它仅定义包的主入口点。

exports 提供了 main 的现代替代方案,允许定义多个入口点、环境之间的条件入口解析支持,并防止除"exports"中定义的入口点之外的任何其他入口点。这种封装允许模块作者清楚地定义其包的公共接口。

对于针对当前支持的 Node.js 版本的新包,建议使用 exports 字段。对于支持 Node.js 10 及更低版本的包, main 字段是必需的。

如果同时定义了 exportsmain ,则在受支持的 Node.js 版本中 exports 字段优先于 main

json
{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./lib": "./lib/index.js",
    "./lib/index": "./lib/index.js",
    "./lib/index.js": "./lib/index.js",
    "./feature": "./feature/index.js",
    "./feature/index": "./feature/index.js",
    "./feature/index.js": "./feature/index.js",
    "./package.json": "./package.json"
  }
}
js
// const message = require('esm-a')
// console.log(message)

// const libMessage = require('esm-a/lib')
// console.log(libMessage)

import message from 'esm-a'
console.log(message)

import libMessage from 'esm-a/lib'
console.log(libMessage)