Node.js 中如何区分 ESM 和 CJS?有哪些方式?
区分方式
1. package.json 的 type 字段
2. 文件扩展名 (.mjs / .cjs)
3. import 语句(ESM)vs require 函数(CJS)
1. package.json type 字段
{
"type": "module"
}
{
"type": "commonjs"
}
2. 文件扩展名(覆盖 type 设置)
.mjs → 始终 ESM(不管 type)
.cjs → 始终 CJS(不管 type)
.js → 取决于 type 字段
3. 代码中的判断
import something from './module.js'
export const value = 1
export default {}
import.meta.url
const something = require('./module')
module.exports = {}
__dirname
__filename
环境差异
console.log(typeof require)
console.log(typeof module)
console.log(__dirname)
console.log(this)
console.log(typeof require)
console.log(__dirname)
console.log(import.meta.url)
console.log(this)
ESM 中使用 __dirname
import { fileURLToPath } from 'url'
import { dirname, join } from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const configPath = join(__dirname, 'config.json')
ESM 中使用 require
import { createRequire } from 'module'
const require = createRequire(import.meta.url)
const cjsModule = require('./legacy.cjs')
顶层 await
const config = await fetch('/config').then(r => r.json())
export default config
JSON 导入
const config = require('./config.json')
import config from './config.json' with { type: 'json' }
import config from './config.json' assert { type: 'json' }
import { readFileSync } from 'fs'
const config = JSON.parse(readFileSync('./config.json', 'utf-8'))
条件导出
{
"exports": {
".": {
"node": {
"import": "./dist/node/index.mjs",
"require": "./dist/node/index.cjs"
},
"browser": {
"import": "./dist/browser/index.mjs",
"require": "./dist/browser/index.cjs"
},
"default": "./dist/index.cjs"
}
}
}
判断当前是 ESM 还是 CJS
if (typeof import.meta !== 'undefined') {
console.log('ESM')
}
if (typeof require !== 'undefined') {
console.log('CJS')
}
let isESM = false
try {
await import('data:text/javascript,')
isESM = true
} catch {
isESM = false
}
最佳实践
- 新项目用 ESM:
"type": "module"
- 库提供双格式:
exports 同时提供 import 和 require
- 混合项目用 .mjs/.cjs:明确区分
- 避免混用:同一项目尽量统一模块格式
同分类其他题目