ESLint 配置 .eslintrc.js 和 .eslintrc.cjs的区别
.eslintrc.js 和 .eslintrc.cjs 的主要区别在于 模块系统 和 文件扩展名约定。以下是详细对比:
📊 核心区别对比表
| 特性 | .eslintrc.js |
.eslintrc.cjs |
|---|---|---|
| 模块系统 | ES Modules (ESM) | CommonJS (CJS) |
| Node.js 支持 | Node.js ≥ 13.2.0(需配置) | 所有 Node.js 版本 |
| 文件扩展名 | .js |
.cjs |
| 使用场景 | 现代项目、ESM 环境 | 传统项目、CJS 环境、混合项目 |
package.json 类型 |
"type": "module" 或无 |
"type": "commonjs" 或无 |
| 导出语法 | export default |
module.exports |
🔧 语法差异
.eslintrc.js (ES Modules)
javascript
// 使用 ES Modules 语法
export default {
env: {
browser: true,
es2021: true
},
extends: ['eslint:recommended'],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module' // 注意:这里是代码的模块类型
},
rules: {
'no-console': 'warn'
}
};
// 或者使用动态配置
import someConfig from './some-config.js';
export default {
extends: [someConfig],
// ... 其他配置
};
.eslintrc.cjs (CommonJS)
javascript
// 使用 CommonJS 语法
module.exports = {
env: {
browser: true,
es2021: true
},
extends: ['eslint:recommended'],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module' // 这里仍然可以是 'module'
},
rules: {
'no-console': 'warn'
}
};
// 或者使用动态配置
const someConfig = require('./some-config.cjs');
module.exports = {
extends: [someConfig],
// ... 其他配置
};
🎯 如何选择?
选择 .eslintrc.cjs 的情况
json
// 如果你的项目有以下特征:
{
"场景": [
"Node.js < 13.2.0",
"package.json 中有 'type': 'commonjs'",
"package.json 中没有 type 字段(默认 CommonJS)",
"项目使用 require() 导入",
"传统项目或库",
"需要兼容性"
]
}
选择 .eslintrc.js 的情况
json
// 如果你的项目有以下特征:
{
"场景": [
"Node.js ≥ 13.2.0",
"package.json 中有 'type': 'module'",
"项目使用 import/export",
"现代前端项目(Vite、现代打包工具)",
"只支持 ESM 的依赖",
"想要使用 ES Modules 特性"
]
}
⚠️ 常见问题与解决方案
问题1:Error [ERR_REQUIRE_ESM]
bash
# 当 package.json 有 "type": "module" 时
# 但 ESLint 尝试用 require() 加载 .eslintrc.js
# 解决方案:重命名为 .eslintrc.cjs
mv .eslintrc.js .eslintrc.cjs
问题2:在 ESM 项目中使用 CJS 配置
javascript
// 项目是 ESM,但想用 CJS 配置 ESLint
// 使用 .eslintrc.cjs 即可正常工作
// package.json
{
"type": "module", // 项目是 ESM
"devDependencies": {
"eslint": "^9.0.0"
}
}
// .eslintrc.cjs ← 使用 .cjs 扩展名
module.exports = {
// CJS 语法
};
问题3:混合项目配置
javascript
// 项目结构示例
project/
├── package.json # "type": "module"
├── .eslintrc.cjs # ESLint 配置(CJS)
├── src/
│ ├── app.js # ESM 代码
│ └── legacy.cjs # CJS 代码
└── lib/
└── commonjs.cjs # CJS 库代码
🔄 转换示例
从 CJS 转换到 ESM
javascript
// 转换前: .eslintrc.js (被当作 CJS)
module.exports = {
env: { node: true },
extends: ['eslint:recommended']
};
// 转换后: .eslintrc.js (作为 ESM)
export default {
env: { node: true },
extends: ['eslint:recommended']
};
// 或者保持为 CJS,但重命名
// .eslintrc.js → .eslintrc.cjs
从 ESM 转换到 CJS
javascript
// 转换前: .eslintrc.js (作为 ESM)
export default {
env: { node: true },
extends: ['eslint:recommended']
};
// 转换后: .eslintrc.cjs
module.exports = {
env: { node: true },
extends: ['eslint:recommended']
};
🛠️ ESLint 8+ 的配置检测逻辑
ESLint 按照以下顺序检测配置文件:
javascript
// ESLint 配置检测流程
1. .eslintrc.js // 尝试作为当前项目类型的模块加载
2. .eslintrc.cjs // 总是作为 CommonJS 加载
3. .eslintrc.yaml
4. .eslintrc.yml
5. .eslintrc.json
6. package.json (eslintConfig 字段)
关键点 :如果 .eslintrc.js 加载失败(因为模块类型问题),ESLint 会尝试其他格式。
📁 实际项目示例
示例1:纯 ESM 项目(Vite/现代前端)
json
// package.json
{
"type": "module",
"devDependencies": {
"eslint": "^9.0.0"
}
}
javascript
// .eslintrc.js 或 .eslintrc.cjs 都可以
// 但推荐 .eslintrc.cjs 避免兼容性问题
// .eslintrc.cjs
module.exports = {
env: { browser: true, es2021: true },
extends: ['eslint:recommended'],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module'
}
};
示例2:Node.js 后端项目
json
// package.json
{
// 没有 type 字段,默认 CommonJS
"devDependencies": {
"eslint": "^9.0.0"
}
}
javascript
// 使用 .eslintrc.js (CJS 语法)
module.exports = {
env: { node: true },
extends: ['eslint:recommended'],
rules: {
'no-console': 'off'
}
};
示例3:混合模块项目
json
// package.json
{
"type": "module",
"exports": {
".": {
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js"
}
}
}
javascript
// .eslintrc.cjs ← 明确使用 CJS
module.exports = {
overrides: [
{
files: ['src/**/*.js'],
parserOptions: { sourceType: 'module' }
},
{
files: ['lib/**/*.cjs'],
parserOptions: { sourceType: 'script' }
}
]
};
🎪 配置优先级与加载机制
优先级顺序(从高到低)
-
.eslintrc.js(根据 package.json 的 type 决定模块类型) -
.eslintrc.cjs(总是 CJS) -
.eslintrc.yaml -
.eslintrc.yml -
.eslintrc.json -
package.json中的eslintConfig
ESLint 的内部判断逻辑
javascript
// 伪代码:ESLint 如何选择配置文件
function loadConfig(filePath) {
const ext = path.extname(filePath);
if (ext === '.js') {
// 根据 package.json 的 type 决定如何加载
if (packageJson.type === 'module') {
return import(filePath); // ESM
} else {
return require(filePath); // CJS
}
}
if (ext === '.cjs') {
return require(filePath); // 总是 CJS
}
// ... 其他格式
}
✅ 最佳实践建议
1. 推荐使用 .eslintrc.cjs
bash
# 无论项目类型,.eslintrc.cjs 都是安全的选择
# 因为:
# - 明确的 CommonJS 标识
# - 兼容所有 Node.js 版本
# - 避免模块类型混淆
mv .eslintrc.js .eslintrc.cjs
2. 明确项目模块类型
json
// 在 package.json 中明确声明
{
"name": "my-project",
"type": "module", // 或省略(默认 CommonJS)
"devDependencies": {
"eslint": "^9.0.0"
}
}
3. 配置示例模板
javascript
// .eslintrc.cjs(通用模板)
module.exports = {
root: true,
env: {
browser: true,
node: true,
es2021: true
},
extends: ['eslint:recommended'],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module' // 这里指解析的代码类型
},
rules: {
// 你的规则
}
};
4. 调试配置加载
bash
# 查看 ESLint 如何解析配置
npx eslint --debug-config
# 或使用 Node.js 调试
NODE_OPTIONS='--loader eslint-flat-config-utils' npx eslint .
🚨 常见错误与解决
错误1:Cannot use import statement outside a module
bash
# 原因:.eslintrc.js 在 CJS 项目中被当作 ESM
# 解决:
rm .eslintrc.js
echo 'module.exports = {}' > .eslintrc.cjs
错误2:ReferenceError: module is not defined
bash
# 原因:.eslintrc.cjs 在 ESM 环境中被错误加载
# 解决:确保文件名是 .cjs,不是 .js
错误3:配置不生效
bash
# 检查配置加载顺序
npx eslint --print-config src/index.js
# 或使用调试模式
npx eslint --debug src/
📦 工具推荐
检测模块类型
bash
# 检查当前文件的模块类型
node -p "require('fs').readFileSync('package.json', 'utf8')" | grep type
# 或使用工具
npm install -g check-esm
check-esm .
自动转换
bash
# 将 .eslintrc.js 转换为 .eslintrc.cjs
# (手动重命名并修改导出语法)
mv .eslintrc.js .eslintrc.cjs
# 然后确保使用 module.exports
总结:
对于大多数项目,推荐使用 .eslintrc.cjs,因为它明确标识为 CommonJS 模块,避免因 Node.js 版本或项目配置导致的模块系统混淆问题。
只有在纯 ESM 项目且明确了解模块系统时,才使用 .eslintrc.js。