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

正确配置这些字段可以:

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

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

相关推荐
spionbo3 分钟前
如何批量下载 vue 文件及相关操作指南
前端
yvvvy6 分钟前
《救命!原生 JS 差点把我 “送走”,直到遇见了 Vue 和 React…》
前端·javascript
每天都想睡觉的19008 分钟前
Vue 的 keep-alive 详解:作用、问题与优化
前端·vue.js
curdcv_po8 分钟前
🫴为什么看大厂的源码,看不到undefined,看到的是void 0
前端
就是我9 分钟前
Electron多窗口应用实战
前端·javascript·electron
芝士加11 分钟前
最全301/302重定向指南:从SEO到实战,一篇就够了
前端·javascript·面试
若梦plus12 分钟前
React19 状态管理方案与原理剖析
前端·react.js
陈随易12 分钟前
2025年100个产品计划之第7个(树图) - 目录结构生成工具
前端·后端·程序员
Joomla中文网13 分钟前
掌握Joomla 4/5自定义库开发:实现PSR-4规范与无缝自动加载
前端
若梦plus14 分钟前
Zustand 使用优化:深入探讨状态管理性能提升
前端·react.js