Skip to content

解决 node-fetch 的 ERR_REQUIRE_ESM 错误

问题描述

当在 Node.js 项目中尝试使用 CommonJS 的 require() 语法导入 node-fetch 时,可能会遇到以下错误:

javascript
const fetch = require('node-fetch');

错误信息:

Error [ERR_REQUIRE_ESM]: require() of ES Module not supported.
Instead change the require to a dynamic import() which is available in all CommonJS modules.

这是因为从 node-fetch v3.0.0 开始,该包已转为仅支持 ES 模块(ESM)格式,不再支持传统的 CommonJS 格式的 require() 导入方式。

解决方案

方案一:降级到 node-fetch v2(推荐方案)

最直接的解决方法是降级到仍然支持 CommonJS 的 v2 版本:

bash
# 卸载当前版本
npm uninstall node-fetch

# 安装 v2 版本
npm install node-fetch@2

安装完成后,您可以继续使用传统的 require() 语法:

javascript
const fetch = require('node-fetch');

TIP

v2 版本仍然会收到关键错误修复,适合需要保持 CommonJS 模块系统的项目。

方案二:使用动态导入

Node.js 支持在 CommonJS 模块中使用动态 import() 语法:

javascript
// 使用动态导入包装 node-fetch
const fetch = (...args) => 
  import('node-fetch').then(({default: fetch}) => fetch(...args));

这种方法允许您在保持 CommonJS 代码结构的同时,使用 ESM 包。

方案三:使用 Node.js 18+ 的内置 fetch

如果您使用的是 Node.js 18.0.0 或更高版本,可以考虑直接使用全局的 fetch API:

javascript
// Node.js 18+ 原生支持 fetch
const res = await fetch('https://api.example.com/data');
if (res.ok) {
  const data = await res.json();
  console.log(data);
}

INFO

Node.js 18 中的 fetch 目前仍处于实验阶段,可以通过 --no-experimental-fetch 标志禁用。

方案四:使用兼容替代包

cross-fetch

cross-fetch 是一个跨环境的 fetch 实现,支持 CommonJS 和 ES 模块:

bash
npm install cross-fetch
javascript
const fetch = require('cross-fetch');

node-fetch-commonjs

这是一个专门为 CommonJS 环境封装的 node-fetch 版本:

bash
npm install node-fetch-commonjs
javascript
const fetch = require('node-fetch-commonjs');

方案五:创建自定义包装器

您可以创建一个自定义的包装模块来处理 ESM 导入:

fetch.js:

javascript
exports.fetch = async function (url, init) {
    const {default: fetch} = await import("node-fetch");
    return await fetch(url, init);
};

使用方式:

javascript
const {fetch} = require("./fetch");

// 然后正常使用 fetch
const response = await fetch('https://api.example.com');

总结

解决方案适用场景优点缺点
降级到 v2需要快速修复简单直接,兼容性好不是最新版本
动态导入想用 v3 但需保持 CommonJS无需降级语法稍复杂
Node.js 内置 fetch使用 Node.js 18+无需额外依赖实验性功能
替代包 (cross-fetch)需要跨环境兼容良好的兼容性基于 node-fetch v2
自定义包装器需要精确控制高度可定制需要额外代码

WARNING

如果您使用 TypeScript,请注意动态导入可能需要额外的类型声明文件来获得完整的类型支持。

选择最适合您项目需求的解决方案。对于大多数情况,降级到 node-fetch@2 或使用 cross-fetch 是最简单可靠的方法。如果您正在启动新项目且使用 Node.js 18+,考虑直接使用内置的 fetch API。