Jacky's blog Jacky's blog
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Jacky Wang

行到水穷处,坐看云起时
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Node

  • VSCode插件

  • Webpack

  • Redux

  • React源码

  • 组件库

  • React高阶系列

  • UMI插件

    • UMI插件-使用插件实现模块与模块的传递
    • UMI插件-注入静态信息
    • package.json 中的 main、types、module、exports 字段
      • 0.前言
      • main 字段与 types 字段
      • module 字段
      • exports 字段
      • 在提案中 module 字段的解析次序如下:
      • 打包器(webpack)中对模块化的处理
      • 参考博文
  • 前端工程化

  • 单元测试vitest

  • 重点技术
  • UMI插件
wangjiasheng
2023-01-11
目录

package.json 中的 main、types、module、exports 字段

# 0.前言

在库开发中,最头疼的就是导出模块的模块语法规范问题。由于现在对 AMD 的需求越来越少了, UMD 格式也逐渐推出历史舞台。因此现在项目组一般要准备两份代码模块,分别是 commonJS 规范以及 esm 规范。

在 package.json 中涉及导出的字段很多,比如说 main、types、module、exports。本篇文章就对这块内容好好做一个梳理工作。

其中还需要解答一个双打包模块的问题,即根据情况,合理去导入 commonjs 语法以及 esm 语法。

# main 字段与 types 字段

这部分主要参考的是: Typescript 在 Publishing 章节官方的说明,见具体网址 (opens new window) 。

这一部分主要涉及到对 .lib.ts 文件的导出。

在这篇文章中说明,有两种方式可以导出声明文件:

  1. 将声明文件与要导出的 npm 包一起导出。
  2. 将声明文件导出给 npm 仓库中的 @types 组织。

虽然 ts 官方推荐第二种方案,但是在项目中显然第一种方案用的更多些。

假设在你的 package 中有一个主入口.js 文件,就可以直接显示在 package.json 中指明对应的主声明文件。将 types 属性设置为指向需要捆绑的声明文件,举例如下:

// package.json
{
  "name": "awesome",
  "author": "Vandelay Industries",
  "version": "1.0.0",
  "main": "./lib/main.js",
  "types": "./lib/main.d.ts"
}
1
2
3
4
5
6
7
8

其中,typings 字段等价于 types,同样也可以被使用。

额外的有,当工程中主文件为 index.js 并且声明文件为 index.d.ts 时,上述声明文件无需显式配置,当然显式声明是推荐的选择。

# module 字段

关于 modules 字段的使用,在stackoverflow 上讨论非常多:

  1. 【What is the "module" package.json field for?】 (opens new window)
  2. 【How to choose 'module' instead of 'main' file in package.json】 (opens new window)

结论如下:

The module field is not officially defined by Node.js and support is not planned. Instead, the Node.js community settled on package exports (opens new window) which they believe is more versatile.

For practical reasons JavaScript bundlers will continue to support the module field. The esbuild docs explain (opens new window) when to use module as well as related fields main and browser.

module 字段一直没有被 Node 官方认可,这也是在 Node document 文档里始终无法查询的原因。module 字段的提出实际上是来源于一个提案 (opens new window),但是实际上官方已经有相应标识 standard module 的字段了,也就是 type: "module" 。 module 只是被打包工具等构建工具使用作为 ES 模块的入口。

# exports 字段

Node 官方 api 使用说明:https://nodejs.org/api/packages.html#exports

但是这里推荐看两篇文章:

  1. Node 官方写的:Conditional exports (opens new window),里面提出了一个 dual package hazard 的概念
  2. Esbuild 官方,在看了 Node 官方的文章后,写一篇总结 (opens new window)。

Conditional exports 提供了一种根据特定条件映射到不同路径的方法,同时支持 commonJs 语法以及 ESM 语法。

例如,一个包想要为 require() 和 import 提供不同的 ES 模块导出可以这样写:

// package.json
{
  "exports": {
    "import": "./index-module.js",
    "require": "./index-require.cjs"
  },
  "type": "module"
}
1
2
3
4
5
6
7
8

除了 import 和 require 可以设置外,还可以设置 default,node,browser 字段。甚至还可以对子包的模块语法进一步划分,官方案例如下:

{
  "exports": {
    ".": "./index.js",
    "./feature.js": {
      "node": "./feature-node.js",
      "default": "./feature.js"
    }
  }
}
1
2
3
4
5
6
7
8
9

当使用 require('pkg/feature.js') 或者 import 'pkg/feature.js' 时,使用 default 来处理位置的 js 环境,当明确为 node 环境时,则导入 ./feature-node.js 文件。

# 在提案中 module 字段的解析次序如下:

提案地址:https://github.com/dherman/defense-of-dot-js/blob/master/proposal.md

To be more specific about how Node.js will decide how to process a given file, once the require algorithm has found it:

  1. If a package does not have a "module" key and has a "main" key, Node.js module resolution for entry points beginning with that package name are unchanged, and Node.js evaluates all files in that package as CommonJS modules.
  2. If a package has no "main" key and has a "module" key, Node.js evaluates all files in that package as standard modules.
  3. If a package has a "modules.root" key, Node.js resolves any requires nested under that package ("lodash/array" in lodash) relative to the "modules.root", and Node.js evaluates all files in that package as standard modules.
  4. If a package has both a "main" key and a "module" key, it can enumerate a list of files ("app/index.js") or directories ("app/routes/") using the "modules" key. Node.js module resolution remains unchanged, but Node.js will evaluate any enumerated files, as well as files inside of enumerated directories, as standard modules.
  5. If a package does not have a "main" or "module" key:
    1. If it has a module.js in the root, it is identical in all respects to the presence of a "module": "module.js" in the package.json.
    2. Otherwise, if it has an index.js in the root, it is identical in all respects to "main": "index.js" in the package.json (the current behavior).
  6. Both require and import use only these rules to decide whether to evaluate a file as a standard module or a CommonJS module.

# 打包器(webpack)中对模块化的处理

在 webpack 中还可以还通过配置 resolve.mainFields 来进行模块化匹配。

如果不主动设置,这个参数实际上是跟着 target 走的。

  • 当 target 为 webworkder、web 时:

    module.exports = {
      //...
      resolve: {
        mainFields: ["browser", "module", "main"], // browser > module > main
      },
    };
    
    1
    2
    3
    4
    5
    6
  • 当 target 设置为 node 环境时,则默认为:

    module.exports = {
      //...
      resolve: {
        mainFields: ["module", "main"], // moudule > main 字段
      },
    };
    
    1
    2
    3
    4
    5
    6

例如,考虑任意一个名为 upstream 的类库 package.json 包含以下字段:

{
  "browser": "build/upstream.js",
  "module": "index"
}
1
2
3
4

当我们 import * as Upstream from 'upstream' 时,这实际上会从 browser 属性解析文件。在这里 browser 属性是最优先选择的,因为它是 mainFields 的第一项。同时,由 webpack 打包的 Node.js 应用程序首先会尝试从 module 字段中解析文件。

简单来说:打包的时候从 module 字段为入口文件,导入时看 browser 字段。

​ --哭了,好复杂啊。

# 参考博文

  1. 【package.json 中 你还不清楚的 browser,module,main 字段优先级 #8】 (opens new window)
编辑 (opens new window)
上次更新: 2023/01/17, 9:01:00
UMI插件-注入静态信息
NPM 发布-检测产物脚本

← UMI插件-注入静态信息 NPM 发布-检测产物脚本→

最近更新
01
如何理解浏览器的 user agent 用户代理的含义?
11-05
02
浏览器事件循环机制
10-31
03
浏览器页面渲染机制【2023】
10-15
更多文章>
Theme by Vdoing | Copyright © 2020-2023
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式