Nor*_*mal 18 javascript node.js
我们从 npm 安装的一些包同时支持 commonjs 和 es 模块,
这些包可以按如下方式导入:
import express from 'express'
// or
const express = require('express')
Run Code Online (Sandbox Code Playgroud)
我创建了一个包,已使用 es 模块将其发布到 npm。
因为我正在开发的另一个项目是用 commonjs 构建的,所以我意识到我不能使用以下语法来要求它:
const stackPlayer = require('stack-player')
Run Code Online (Sandbox Code Playgroud)
我怎样才能支持我的包stack-player中的两个模块系统,以便世界各地的每个人都可以使用它?
Aur*_*ast 21
主要有两种场景:
这意味着您的包用于require()
加载依赖项。对于这种包,不需要特殊的工作来支持在 ES 和 CJS 模块中加载包。ES 模块能够通过该import
语句加载 CJS 模块,但需要注意的是仅支持默认导入语法。CJS模块可以通过该require()
函数加载其他CJS模块。所以ES模块和CJS模块都能够加载CJS模块。
这意味着您的包用于import
加载依赖项。但不要被愚弄 - 有时,尤其是在使用 TypeScript 时,您可能正在编写import
代码,但它会在require()
幕后编译。
不幸的是,CommonJS 模块不支持加载 ES 模块,除非(在 Node.js 中)使用该import()
函数(这有点痛苦,不是一个很好的解决方案)。
为了在这种情况下支持 CommonJS,最好的选择是将包转换为 CommonJS 模块,并同时提供包的 CommonJS 和 ESM 版本。
我在自己的一些包中主要使用 Rollup 来完成此操作,这使得它相对容易。
基本概念是这样的:
npm i -D rollup
npx rollup index.js --file index.cjs --format cjs
将您的代码转换为 CJS 模块。{
"name": "my-package",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"exports": {
"import": "./index.js",
"require": "./index.cjs"
}
}
Run Code Online (Sandbox Code Playgroud)
这样,CJS 模块加载器知道加载您的index.cjs
文件,而 ESM 加载器知道加载您的index.js
文件,两者都很高兴。
require()
默认情况下,只能在CommonJS Modules中使用。将 ECMAScript 模块导入 CommonJS 的内置方法是使用import(pathToFile).then(module => { })
.
如果您想支持require()
您的包,则必须提供CommonJS 模块。
这是一个功能示例,演示了何时以及如何使用require()
or import()
。import()
与 ECMAScript 模块相比,CommonJS 模块的工作方式存在一些细微的差异。特别是,当在import()
导出带有module.exports
.
index.js
它导入不同的模块类型(来自上面的演示):(
以防 stackblitz 演示将被删除:)
// executed as CommonJS module
console.time('');
import('./lib/example.cjs').then(({ default: example }) => {
console.timeLog('', 'import cjs', example() == 'Foo'); // true
});
import('./lib/index.mjs').then(({ example }) => {
console.timeLog('', 'import mjs', example() == 'Foo'); // true
});
try {
const example = require('./lib/example.cjs');
console.timeLog('', 'require cjs', example() == 'Foo'); // true
} catch (e) {
console.timeLog('', 'require cjs', '\n' + e.message);
}
try {
const example = require('./lib/index.mjs');
console.timeLog('', 'require mjs', example() == 'Foo');
} catch (e) {
console.timeLog('', 'require mjs', '\n' + e.message); // Error [ERR_REQUIRE_ESM]: require() of ES Module /path/to/lib/index.mjs not supported.
}
Run Code Online (Sandbox Code Playgroud)
lib/example.cjs
module.exports = function example() {
return 'Foo';
};
Run Code Online (Sandbox Code Playgroud)
lib/index.mjs
import example from './example.cjs';
export { example };
export default example;
Run Code Online (Sandbox Code Playgroud)
可以为要支持的包提供条件导出require()
,例如在require()
您的包不再支持 CommonJS 的情况下。请参阅此链接了解更多信息。
“exports”字段允许在通过 node_modules 查找或对其自身名称的自引用加载的名称导入时定义包的入口点。Node.js 12+ 支持它作为“main”的替代方案,可以支持定义子路径导出和条件导出,同时封装内部未导出的模块。
package.json
(来自nodejs文档的示例)
{
"exports": {
"import": "./index-import.js",
"require": "./index-require.cjs"
},
"type": "module"
}
Run Code Online (Sandbox Code Playgroud)
如果是这样,您必须提供两个脚本:一个用于 CommonJS ( "require": "filename"
),另一个用于 ECMAScript 模块 ( "import": "filename"
)。
虽然必须通过或index-require.js
提供脚本,但必须通过 提供脚本。exports = ...
module.exports = ...
index-import.js
export default
您只能根据文件模块类型使用特定关键字。
module.exports
用于定义模块导出并可供其他模块使用的值。它可以设置为任何值,包括对象、函数或简单数据类型(如字符串或数字)。exports
,module
require()
require()
ECMAScript 模块内部是可能的,但是您必须使用此答案中提到的解决方法或查看以下文档module.createRequire(fileName)
:
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
// sibling-module.js is a CommonJS module.
const siblingModule = require('./sibling-module');
Run Code Online (Sandbox Code Playgroud)
如果您require()
从 ECMAScript 模块上的 CommonJS 中调用,则会抛出不支持的错误:
Error [ERR_REQUIRE_ESM]: require() of ES Module /path/to/script.mjs not supported.
Run Code Online (Sandbox Code Playgroud)
根据情况提供更详细的错误消息:
相反,将 /path/to/app.js 中 script.mjs 的 require 更改为所有 CommonJS 模块中都可用的动态 import() 。
或者:
/path/to/script.js 被视为 ES 模块文件,因为它是一个 .js 文件,其最近的父 package.json 包含 "type": "module" ,该文件将该包范围内的所有 .js 文件声明为 ES 模块。
相反,将 /path/to/script.js 重命名为以 .cjs 结尾,更改所需代码以使用所有 CommonJS 模块中可用的动态 import(),或将 "type": "module" 更改为 "type": "commonjs " 在 /path/to/package.json 中将所有 .js 文件视为 CommonJS (对所有 ES 模块使用 .mjs 代替)。
export default
用于导出单个值作为模块的默认导出。这允许以更简洁的方式导入值,因为导入语句在导入默认导出时可以省略大括号。export
关键字,后跟标识符和值。( export const foo = "bar"
)import ... from ...
它可以处理 CommonJS 文件并解释它们,就像您使用require()
.
基于express的示例:
import express, { Route, Router } from 'express'; // EJS
// is similar to:
var express = require("express"), { Route, Router } = express; // CJS
Run Code Online (Sandbox Code Playgroud)
CommonJS 和 ECMAScript 模块都支持该import()
函数,但返回的对象可以具有 ESM 文件的更多属性。
CJS 模块不需要转换为 ESM,因为可以使用import ... from ...
语法将它们导入 ESM,而无需对 CJS 模块进行任何修改。但是,建议使用 ECMAScript 模块语法编写新模块,因为它是 Web 和服务器端应用程序的标准,并且可以在浏览器/客户端和节点/服务器端无缝使用相同的代码。
此外,我发现logrocket.com 上关于 Node.js 中的 CommonJS 与 ES 模块的这篇文章内容非常丰富。它更深入地探讨了 ECMAScript 与 CommonJS 相比的优缺点。