解决 Node.js 中的 ERR_REQUIRE_ESM 错误
问题概述
当你在 Node.js 中使用 require()
导入某些包时,可能会遇到以下错误:
error [ERR_REQUIRE_ESM]: require() of ES Module [...] is not supported.
Instead change the require of index.js to a dynamic import() which is available in all CommonJS modules
这个错误通常发生在尝试使用 CommonJS 的 require()
语法导入仅支持 ES 模块 (ESM) 的包时。在问题示例中,错误是由 node-fetch
包引起的,因为从第 3 版开始,它只支持 ES 模块。
解决方案
以下是几种解决 ERR_REQUIRE_ESM 错误的方法,从推荐到不推荐排序:
方案一:使用动态导入(推荐)
动态导入 import()
在 CommonJS 模块中可用,是最灵活的解决方案:
// 将 const fetch = require('node-fetch') 替换为:
const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
对于其他 ESM-only 包,可以使用类似的模式:
// 通用模式
(async () => {
const package = await import('package-name').then(pkg => pkg.default);
// 使用 package...
})().catch(console.error);
方案二:迁移到 ES 模块
如果你控制着整个项目,可以将其完全转换为 ES 模块:
- 在
package.json
中添加:
{
"type": "module"
}
- 将所有
require()
语句改为import
语句:
// 替换前: const fs = require('fs');
import fs from 'fs';
方案三:降级包版本(临时解决方案)
如果暂时无法使用上述方法,可以降级到支持 CommonJS 的旧版本:
npm install node-fetch@2.6.1
WARNING
降级不是长期解决方案,因为你将错过新特性和安全更新。
方案四:使用兼容性包
有些包提供了兼容性版本,例如对于 node-fetch
:
npm install node-fetch-native
完整代码示例
以下是使用动态导入修复后的完整代码:
const FormData = require('form-data');
const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
const path = require("path")
const basePath = process.cwd();
const fs = require("fs");
fs.readdirSync(`${basePath}/build/images`).forEach(file => {
const formData = new FormData();
const fileStream = fs.createReadStream(`${basePath}/build/images/${file}`);
formData.append('file', fileStream);
let url = 'https://api.nftport.xyz/v0/files';
let options = {
method: 'POST',
headers: {
Authorization: '[...]',
},
body: formData
};
fetch(url, options)
.then(res => res.json())
.then(json => {
const fileName = path.parse(json.file_name).name;
let rawdata = fs.readFileSync(`${basePath}/build/json/${fileName}.json`);
let metaData = JSON.parse(rawdata);
metaData.file_url = json.ipfs_url;
fs.writeFileSync(`${basePath}/build/json/${fileName}.json`, JSON.stringify(metaData, null, 2));
console.log(`${json.file_name} uploaded & ${fileName}.json updated!`);
})
.catch(err => console.error('error:' + err));
})
根本原因
Node.js 支持两种模块系统:
- CommonJS: 使用
require()
和module.exports
- ES 模块 (ESM): 使用
import
和export
越来越多的包开发者选择只提供 ESM 版本,因为这是 JavaScript 的标准模块系统,并且具有更好的 tree-shaking 特性。
最佳实践
- 优先使用动态导入:这是最灵活的解决方案,既支持 CommonJS 也支持 ESM
- 逐步迁移到 ESM:长期来看,ES 模块是 JavaScript 的未来
- 检查包文档:在安装新包时,查看其文档了解模块系统要求
- 保持依赖更新:避免使用过时的包版本,以免错过安全更新
INFO
TypeScript 用户注意:如果你的项目使用 TypeScript,确保 tsconfig.json
中的 module
设置正确。对于 CommonJS 项目使用 "module": "commonjs"
,对于 ESM 项目使用 "module": "ESNext"
。
通过上述方法,你应该能够成功解决 Node.js 中的 ERR_REQUIRE_ESM 错误,并继续你的开发工作。