Skip to content

解决 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 模块中可用,是最灵活的解决方案:

javascript
// 将 const fetch = require('node-fetch') 替换为:
const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));

对于其他 ESM-only 包,可以使用类似的模式:

javascript
// 通用模式
(async () => {
  const package = await import('package-name').then(pkg => pkg.default);
  // 使用 package...
})().catch(console.error);

方案二:迁移到 ES 模块

如果你控制着整个项目,可以将其完全转换为 ES 模块:

  1. package.json 中添加:
json
{
  "type": "module"
}
  1. 将所有 require() 语句改为 import 语句:
javascript
// 替换前: const fs = require('fs');
import fs from 'fs';

方案三:降级包版本(临时解决方案)

如果暂时无法使用上述方法,可以降级到支持 CommonJS 的旧版本:

bash
npm install node-fetch@2.6.1

WARNING

降级不是长期解决方案,因为你将错过新特性和安全更新。

方案四:使用兼容性包

有些包提供了兼容性版本,例如对于 node-fetch

bash
npm install node-fetch-native

完整代码示例

以下是使用动态导入修复后的完整代码:

javascript
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): 使用 importexport

越来越多的包开发者选择只提供 ESM 版本,因为这是 JavaScript 的标准模块系统,并且具有更好的 tree-shaking 特性。

最佳实践

  1. 优先使用动态导入:这是最灵活的解决方案,既支持 CommonJS 也支持 ESM
  2. 逐步迁移到 ESM:长期来看,ES 模块是 JavaScript 的未来
  3. 检查包文档:在安装新包时,查看其文档了解模块系统要求
  4. 保持依赖更新:避免使用过时的包版本,以免错过安全更新

INFO

TypeScript 用户注意:如果你的项目使用 TypeScript,确保 tsconfig.json 中的 module 设置正确。对于 CommonJS 项目使用 "module": "commonjs",对于 ESM 项目使用 "module": "ESNext"

通过上述方法,你应该能够成功解决 Node.js 中的 ERR_REQUIRE_ESM 错误,并继续你的开发工作。