package.json 中模块入口字段详解

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
  • 静态分析优化
  • 现代打包工具优先使用

打包工具处理顺序:

  1. 检查module字段
  2. 不存在则回退到main
  3. 最后尝试index.js

2.3 browser 字段

作用:定义浏览器专用入口或替换特定模块。

json 复制代码
{
  "browser": {
    "./lib/node.js": "./lib/browser.js",
    "fs": false
  }
}

使用场景:

  1. 替换Node.js特有API的浏览器实现
  2. 禁用某些不能在浏览器使用的模块
  3. 提供轻量级浏览器版本

示例工作流程:

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 多环境适配策略

  1. ESM 和 CJS 双输出:

    bash 复制代码
    src/
    ├── index.ts       # 源代码
    dist/
    ├── index.cjs.js   # CommonJS 版本
    ├── index.esm.js   # ES Module 版本
    └── index.umd.js   # 通用浏览器版本
  2. 构建脚本示例:

    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 逐步迁移策略

  1. 先保持main字段确保兼容性
  2. 添加module字段支持现代打包工具
  3. 逐步引入exports条件导出
  4. 最后考虑移除旧字段(需测试)

总结

  1. 演进趋势 :从main到exports是模块定义的发展方向
  2. 兼容策略:新老字段组合使用确保平滑过渡
  3. 最大优势 :exports提供了最精细的控制能力
  4. 发布检查 :使用pkg-exports工具验证配置正确性

正确配置这些字段可以:

  • 显著提升Tree Shaking效率
  • 优化浏览器和Node.js的不同构建
  • 提供更好的类型提示
  • 增强模块封装性和安全性

对于新项目,推荐优先使用exports字段;对于已有项目,可以逐步迁移到现代配置方案。

相关推荐
IT_陈寒16 分钟前
当Java的hashCode遇上Lombok,我的缓存被全清了
前端·人工智能·后端
工控丫丫43 分钟前
帆软看板中经常使用的公式函数及经常搭的组合
前端·javascript·数据库
jerrywus1 小时前
我用 GPT-6.1-sol 做了台会讲解的 3D 售货机:60 罐饮料,一口没喝上
前端·agent·claude
孟陬2 小时前
Foobar 技术周刊#159:当看不懂代码时,如何高效排障调试。脏页陷阱:一条只读查询,写入数 TB 数据。推测解码:大模型加速利器
前端·人工智能·后端
Zhao-Jichao2 小时前
AI Prompting for Everyone 课程笔记 - P17 图像生成 Imaging Generation
前端
Highcharts.js3 小时前
智能数据分析组件Orbit 正式发布与快速上手体验教程
前端·人工智能·信息可视化·数据分析系统·highcharts·orbit·智能可视化
螺丝钉tina4 小时前
[Playwright ][16]Playwright + TypeScript API 自动化测试实战:API测试
前端·javascript·typescript
小天源5 小时前
智叟云集系统 · 架构与职责分层
服务器·前端·数据库
Highcharts.js6 小时前
React 中原生支持使用 JSX 组件配置自定义表格 Highcharts Grid
前端·javascript·react.js·前端框架·jsx·highcharts·自定义表格
薛定谔的猫-菜鸟程序员6 小时前
Electron开发超详细教程(从入门到放弃)
前端·javascript·electron