package.json 中的 type: module 作用详解
问题描述
当升级 Node.js 并尝试构建现有项目时,可能会遇到以下错误:
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module:
│ ~~/nuxt.config.js
│ require() of ES modules is not supported.
│ require() of ~~/nuxt.config.js from
│ ~~/config.js is an ES
│ module file as it is a .js file whose nearest parent package.json contains "type":
│ "module" which defines all .js files in that package scope as ES modules.
│ Instead rename nuxt.config.js to end in .cjs, change the requiring code to use
│ import(), or remove "type": "module" from
│ ~~/package.json.
这个错误是因为项目中的 package.json
文件包含了 "type": "module"
声明,这改变了 Node.js 处理 JavaScript 模块的方式。
什么是 type: module?
根据 Node.js 官方文档,"type"
字段定义了 Node.js 如何处理项目中的 .js
文件:
- 当最近的
package.json
包含"type": "module"
时,.js
文件会被当作 ES 模块处理 - 当最近的
package.json
缺少"type"
字段,或包含"type": "commonjs"
时,.js
文件会被当作 CommonJS 模块处理
简单来说
- 使用
require()
和module.exports
→ 需要 CommonJS(默认或无 type 字段) - 使用
import
和export
→ 需要 ES 模块("type": "module"
)
解决方案
方案一:保留 type: module 并修改代码
如果你希望使用现代 ES 模块语法,应该保留 "type": "module"
并修改代码:
// 使用 ES 模块的 import 语法
import express from 'express';
// 而不是 CommonJS 的 require()
// const express = require('express');
方案二:移除 type: module
如果你希望继续使用传统的 CommonJS 语法,可以移除 package.json
中的 "type": "module"
:
{
// 移除或注释掉这行
// "type": "module",
"name": "your-project",
"version": "1.0.0",
// 其他配置...
}
注意事项
移除 type: module
会使所有 .js
文件恢复为 CommonJS 模块,确保你的代码中不使用 ES 模块的 import/export
语法。
方案三:混合使用文件扩展名
Node.js 支持通过文件扩展名区分模块类型:
.js
- 根据package.json
中的type
字段决定.mjs
- 始终作为 ES 模块处理.cjs
- 始终作为 CommonJS 模块处理
你可以将特定文件重命名:
# 将 ES 模块文件重命名为 .mjs
mv nuxt.config.js nuxt.config.mjs
# 或者将 CommonJS 文件重命名为 .cjs
mv config.js config.cjs
方案四:使用 Node.js 新特性(v20.10.0+)
对于 Node.js v20.10.0 或更高版本,提供了新的实验性选项:
# 自动检测模块类型
node --experimental-detect-module index.js
# 或将默认类型设置为模块
node --experimental-default-type="module" index.js
你可以在 package.json
的脚本中配置:
{
"scripts": {
"start": "node --experimental-detect-module index.js",
"dev": "node --experimental-default-type=\"module\" index.js"
}
}
方案五:使用输入类型标志
对于 CI/CD 管道或临时执行,可以使用 --input-type
标志:
# 直接将 ES 模块代码传递给 Node.js
echo "import { myFunc } from 'my-package';" | node --input-type=module
最佳实践建议
- 新项目:推荐使用
"type": "module"
并采用 ES 模块语法 - 现有项目:根据代码库现状决定是否迁移到 ES 模块
- 混合项目:使用文件扩展名(
.mjs
/.cjs
)区分模块类型 - Node.js 版本:确保使用支持的 Node.js 版本(v16+ 为 LTS)
版本兼容性说明
- Node.js v12+:基本支持 ES 模块(需要标志)
- Node.js v14+:改进的 ES 模块支持
- Node.js v16+:稳定的 ES 模块支持(LTS)
- Node.js v20.10.0+:新增自动检测模块类型功能
常见问题解答
Q: 移除 type: module 会有问题吗? A: 如果你的代码使用的是 require()
语法,移除 type: module
是安全的。但如果代码中使用了 import/export
,则需要修改代码。
Q: 如何判断我的代码使用的是哪种模块系统? A: 检查代码中的导入/导出语法:
const module = require('module')
和module.exports = {}
→ CommonJSimport module from 'module'
和export default module
→ ES 模块
Q: 可以在同一个项目中混合使用两种模块系统吗? A: 可以,但需要通过文件扩展名(.mjs
用于 ES 模块,.cjs
用于 CommonJS)或使用动态导入来实现。
通过理解 package.json
中 type: module
的作用,你可以更好地管理项目的模块系统,避免常见的兼容性问题。