package 文件中的 main、module、exports 字段
package 文件中的 main、module、exports 字段
main
main 字段的值是一个模块 id(默认是 index.js),是程序的主要入口点。也就是说,如果我们的包名为 foo,当其他开发者安装 foo 这个包,然后通过 require(‘foo’) 、import ‘foo’ 方式导入,那么 foo 包中 package.json 中的 main 的导出对象将被返回。这应该是相对于包文件夹根的模块 id。require(‘foo’) 相当于 require(‘./node_modules/foo/index.js’)。
{"main": "index.js",
}
require('foo');
// 相当于
// require('./node_modules/foo/index.js')
main 字段通常指定一个不需要 tree-shaking 的 bundle 文件。
module
module 字段是为了解决 ES 模块(ES6 import)的需要而引入的。它通常指向一个经过编译,适合在浏览器或支持动态导入的环境使用的ES模块版本。在这些环境中,如使用 import 导入模块时,系统会优先查找 module 字段指定的文件。例如,如果我们 foo 包 module 字段设置为 “index.esm.js”,而 main 字段设置为 ‘index.js’,那么通过 import ‘foo’ 和 require(‘foo’) 导入的文件是不同的。
{"main": "index.js","module": "index.esm.js"
}
import 'foo';
// 相当于
// import './node_modules/foo/index.esm.js'require('foo');
// 相当于
// require('./node_modules/foo/index.js')
虽然表明上只是用于区别 import 和 require 的写法,但是 module 还有一个非常重要的指示作用,就是告诉其他开发者 module 指定的文件是可以进行 Tree Shaking,比如我们只需要包中的某个方法,通过 import 导入 module 后,在打包的时候会去除掉其他未使用的代码,而 main 指定的文件通常包含了整个 bundle 文件,不好进行 tree shaking,如果我们导入的是 main 文件,那么在打包的时候就会将全部内容都打包进去。
exports
exports 字段是 Node 13.2 版本引入的新特性,用于提供更灵活的导出控制。它允许你指定多个导出路径,甚至可以对不同导入方式进行差异化处理。并且当它存在时,它的优先级最高。
在前面介绍 main、module 两个字段,只向外部暴露了一个文件,也就是我们通过 require、import 只能导入一个文件。通过配置 exports,我们可以导出更多文件。
{"main": "index.js","module": "index.esm.js",
}// 上面的写法相当于
{
"exports": {".": {"require": "./index.js","import": "./index.esm.js"}}
}
为什么要加一个层级,把 require 和 import 放在 “.” 下面呢?
因为 exports 除了支持配置包的默认导出,还支持配置包的子路径。
比如我们想导入 foo 包下的某个文件:
require('foo/dist/index.css')
会报错:
Package subpath './dist/index.css' is not defined by "exports" in xxxxx
我们可以在 exports 中设置:
{
"exports": {".": {"require": "./index.js","import": "./index.esm.js"},"./dist/*": "./dist/*"}
}
在 exports 中,可以定义如下几个字段:
- node-addons:类似 node,适用于任何 node.js 环境。此条件可用于提供一个使用本机 C++ 插件的入口点,而不是一个更通用、不依赖本机插件的入口。可以通过 --no addons标志禁用此条件。
- node:匹配任何 node.js 环境。可以是 CommonJS 或 ES 模块文件。
-
- 可以配置 production、development、default
{"exports": {".": {"require": {"node": {"production": "./index.prod.js","development": "./index.dev.js","default": "./index.js"}}}} }
- 可以配置 production、development、default
- import: 通过 import 或 import() 或 ECMAScript 模块加载器的任何顶级导入或解析操作加载包时匹配。无论目标文件的模块格式如何,都适用。总是与 require 相互排斥。
- require:通过 require() 加载包时匹配。引用的文件应该可以使用 require() 加载,尽管无论目标文件的模块格式如何,条件都匹配。如果启用了 --experimental-require-module,则预期的格式包括 CommonJS、JSON、本机插件和 ES 模块。始终与 import 相互排斥。
- default:始终匹配的通用回退。可以是 CommonJS 或 ES 模块文件。这种情况应该总是最后出现。
这几个字段的顺序是非常重要的,在条件匹配过程中,前面的 key 具有更高的优先级。一般规则是,条件应按对象顺序从最具体到最不具体。
