Skip to content

ES模块作用域中 require 未定义的解决方案

问题描述

当在 Node.js 项目中遇到以下错误时:

js
ReferenceError: require is not defined in ES module scope, you can use import instead

通常是因为项目或文件被配置为 ES 模块(ECMAScript Modules),但代码中仍在使用 CommonJS 的 require() 语法。

具体到使用 gulp-sass 的场景:

js
const sass = require('gulp-sass')(require('sass'));

错误信息明确指出:由于 package.json 中包含 "type": "module" 且文件扩展名为 .js,该文件被当作 ES 模块处理。

根本原因

Node.js 支持两种模块系统:

  • CommonJS:使用 require()module.exports
  • ES 模块:使用 importexport

package.json 设置 "type": "module" 时,所有 .js 文件都被视为 ES 模块,无法使用 require()

解决方案

方案一:修改 package.json(推荐用于传统项目)

如果项目主要是 CommonJS 模块,最简单的方法是修改 package.json

json
{
  "type": "commonjs"
}

这样所有 .js 文件将使用 CommonJS 模块系统,require() 语法可以正常工作。

注意事项

修改模块类型可能会影响项目中其他依赖 ES 模块的库,请确保兼容性。

方案二:使用 ES 模块导入语法(推荐用于新项目)

将代码中的 require() 替换为 ES 模块的 import 语法:

js
// 替换前
const sass = require('gulp-sass')(require('sass'));

// 替换后
import gulpSass from 'gulp-sass';
import dartSass from 'sass';
const sass = gulpSass(dartSass);

同时确保 package.json 中有 "type": "module" 设置。

方案三:使用 .cjs 文件扩展名

如果只想在特定文件中使用 CommonJS,可以将文件扩展名改为 .cjs

js
// 将 gulpfile.js 重命名为 gulpfile.cjs

这样即使 package.json 设置了 "type": "module",该文件也会被当作 CommonJS 模块处理。

方案四:使用 createRequire 方法(混合模块环境)

在 ES 模块中使用 createRequire 来加载 CommonJS 模块:

js
import { createRequire } from 'module';
const require = createRequire(import.meta.url);

const sass = require('gulp-sass')(require('sass'));

这种方法适合在 ES 模块项目中需要兼容个别 CommonJS 包的情况。

解决 yargs 相关问题

原始问题中提到的第二个错误:

js
TypeError: Cannot read property 'prod' of undefined

这是由于在 ES 模块中,yargs.argv 的访问方式发生了变化。解决方案:

js
// CommonJS 方式
const PRODUCTION = yargs.argv.prod;

// ES 模块方式
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';

const argv = yargs(hideBin(process.argv)).argv;
const PRODUCTION = argv.prod;

其他常见场景

TypeScript 和 Jest 环境

在 TypeScript 和 Jest 测试环境中,可能需要特殊配置:

js
function legacyRequireImport(path) {
  if (process.env.NODE_ENV === "test") {
    return require(path);
  }
  const _require = createRequire(import.meta.url);
  return _require(path);
}

const someModule = legacyRequireImport("some_module");

jest.config.js 中需要相应配置以支持这种混合模式。

Node.js 版本兼容性

确保 Node.js 版本与项目配置兼容。可以通过 .nvmrc 文件指定 Node.js 版本:

bash
node --version > .nvmrc

或使用 nvm 管理多版本:

bash
nvm install 20
nvm use 20

总结

场景解决方案适用情况
完全使用 CommonJS设置 "type": "commonjs"传统项目,大量使用 require
完全使用 ES 模块使用 import/export 语法新项目,现代前端开发
混合模块环境使用 createRequire 或文件扩展名过渡期项目,需要兼容两种模块

最佳实践建议

  1. 新项目推荐使用 ES 模块语法
  2. 现有项目可根据依赖库的兼容性决定是否迁移
  3. 大型项目迁移时可采用渐进式策略,逐步替换 require 为 import

通过正确配置模块系统和统一代码风格,可以避免 "require is not defined" 错误,确保项目正常运行。