package.json 中模块入口字段详解
在 Node.js 和前端生态系统中,package.json
中的入口字段决定了模块如何被不同环境加载。这些字段的合理配置对模块的兼容性和性能至关重要。
一、核心字段对比
字段 | 适用场景 | 加载器 | 典型用途 |
---|---|---|---|
main |
通用 | CommonJS/Node | 传统Node.js模块入口 |
module |
现代打包工具 | ES Module | 提供ESM版本以支持Tree Shaking |
browser |
浏览器环境 | 打包工具 | 提供浏览器专用版本或替换Node模块 |
exports |
Node 12+ | 条件加载 | 现代多环境入口配置 |
二、字段详解与配置示例
2.1 main 字段
作用:定义CommonJS模块的主入口文件,是Node.js和传统打包工具的默认入口。
json
{
"main": "dist/index.cjs.js"
}
特点:
- Node.js 原生支持
- Webpack/Rollup 默认回退字段
- 必须使用CommonJS模块语法
加载逻辑:
graph TD
A[require('pkg')] --> B{是否有main字段?}
B -->|是| C[加载main指定文件]
B -->|否| D[尝试加载index.js]
2.2 module 字段
作用:指定ES Module格式的入口文件,供支持ESM的打包工具使用。
json
{
"module": "dist/index.esm.js"
}
优势:
- 支持Tree Shaking
- 静态分析优化
- 现代打包工具优先使用
打包工具处理顺序:
- 检查
module
字段 - 不存在则回退到
main
- 最后尝试
index.js
2.3 browser 字段
作用:定义浏览器专用入口或替换特定模块。
json
{
"browser": {
"./lib/node.js": "./lib/browser.js",
"fs": false
}
}
使用场景:
- 替换Node.js特有API的浏览器实现
- 禁用某些不能在浏览器使用的模块
- 提供轻量级浏览器版本
示例工作流程:
graph LR
A[浏览器构建] --> B[检测browser字段]
B -->|路径映射| C[替换为浏览器版本]
B -->|false| D[移除该模块]
2.4 exports 字段 (Node 12+)
作用:提供更精细的入口控制,支持条件导出。
json
{
"exports": {
".": {
"import": "./dist/index.esm.js",
"require": "./dist/index.cjs.js",
"default": "./dist/index.umd.js"
},
"./features": {
"import": "./features/index.mjs",
"require": "./features/index.cjs"
},
"./package.json": "./package.json"
}
}
高级特性:
- 条件导出(根据环境自动选择)
- 子路径导出(限制可访问路径)
- 默认回退(default)
- 封装性(未列出的路径不可访问)
条件导出支持的环境:
import
- ESM加载require
- CJS加载browser
- 浏览器环境node
- Node.js环境default
- 通用回退
三、字段优先级与解析规则
3.1 现代打包工具解析顺序
graph TD
A[import/require模块] --> B{是否有exports字段?}
B -->|是| C[按条件选择对应入口]
B -->|否| D{是否有module字段?}
D -->|是| E[使用ESM版本]
D -->|否| F[使用main字段]
F -->|无main| G[查找index.js]
3.2 Node.js 不同版本行为
Node版本 | 默认行为 |
---|---|
< 12 | 仅识别main |
12-13 | 实验性支持exports |
≥ 14 | 完整支持exports |
四、最佳实践配置
4.1 通用库配置示例
json
{
"name": "my-library",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
"browser": "dist/index.umd.js",
"exports": {
".": {
"import": "./dist/index.esm.js",
"require": "./dist/index.cjs.js",
"browser": "./dist/index.umd.js"
},
"./lite": {
"import": "./dist/lite.esm.js",
"require": "./dist/lite.cjs.js"
}
},
"types": "dist/index.d.ts"
}
4.2 多环境适配策略
-
ESM 和 CJS 双输出:
bashsrc/ ├── index.ts # 源代码 dist/ ├── index.cjs.js # CommonJS 版本 ├── index.esm.js # ES Module 版本 └── index.umd.js # 通用浏览器版本
-
构建脚本示例:
json{ "scripts": { "build:cjs": "tsc --module commonjs --outDir dist/cjs", "build:esm": "tsc --module esnext --outDir dist/esm", "build:umd": "rollup -c rollup.config.js", "build": "npm run build:cjs && npm run build:esm && npm run build:umd" } }
五、常见问题解决方案
5.1 向后兼容问题
问题 :老版本Node.js无法识别exports
解决方案:
json
{
"main": "dist/legacy.js",
"exports": {
".": {
"node": {
"require": "./dist/modern.cjs.js",
"import": "./dist/modern.mjs"
},
"default": "./dist/modern.umd.js"
}
}
}
5.2 子路径访问控制
限制 :使用exports
后所有子路径必须显式声明
json
{
"exports": {
".": "./index.js",
"./features": "./features/index.js",
"./package.json": "./package.json"
}
}
此时require('pkg/utils')
将报错,除非在exports
中声明
5.3 类型定义配合
推荐配置:
json
{
"types": "dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.ts",
"default": "./dist/index.cjs"
}
}
}
}
六、进阶用法
6.1 条件导出扩展
json
{
"exports": {
".": {
"development": "./dev/index.js",
"production": "./prod/index.js",
"test": "./test/index.js",
"default": "./dist/index.js"
}
}
}
6.2 嵌套条件逻辑
json
{
"exports": {
".": {
"node": {
"import": "./node.mjs",
"require": "./node.cjs"
},
"browser": {
"import": "./browser.mjs",
"require": "./browser.cjs"
},
"default": "./index.js"
}
}
}
七、工具支持情况
7.1 打包工具支持度
工具 | exports |
module |
browser |
---|---|---|---|
Webpack 5 | ✔️ | ✔️ | ✔️ |
Rollup | ✔️ | ✔️ | ✔️ |
Parcel 2 | ✔️ | ✔️ | ✔️ |
esbuild | ✔️ | ✔️ | ✔️ |
Vite | ✔️ | ✔️ | ✔️ |
7.2 Node.js版本支持
特性 | 引入版本 |
---|---|
基础exports |
12.0.0 |
条件导出 | 12.16.0 |
模式匹配 | 14.13.0 |
导入属性 | 18.0.0 |
八、迁移指南
8.1 从传统配置升级
旧版:
json
{
"main": "index.js",
"browser": "browser.js"
}
现代化改造:
json
{
"main": "./index.cjs",
"module": "./index.mjs",
"exports": {
".": {
"require": "./index.cjs",
"import": "./index.mjs",
"browser": "./browser.js"
}
}
}
8.2 逐步迁移策略
- 先保持
main
字段确保兼容性 - 添加
module
字段支持现代打包工具 - 逐步引入
exports
条件导出 - 最后考虑移除旧字段(需测试)
总结
- 演进趋势 :从
main
到exports
是模块定义的发展方向 - 兼容策略:新老字段组合使用确保平滑过渡
- 最大优势 :
exports
提供了最精细的控制能力 - 发布检查 :使用
pkg-exports
工具验证配置正确性
正确配置这些字段可以:
- 显著提升Tree Shaking效率
- 优化浏览器和Node.js的不同构建
- 提供更好的类型提示
- 增强模块封装性和安全性
对于新项目,推荐优先使用exports
字段;对于已有项目,可以逐步迁移到现代配置方案。