如果你打算发布一个 js 库,那么你需要理解 package.json 里的 main, module, exports, type:module.
- type:"module": 你的项目里的
*.js
文件必须使用es module 语法, 如import xxx from 'xxx'
. 如果你要使用commonjs语法, 如require('xxx')
, 则需要把*.js
文件后缀改为.cjs
.- 相反, 如果没有
type:"module"
, 则'*.js'文件里的代码必须使用commonjs语法. 要使用es module 语法, 则需要把*.js
文件后缀改为.mjs
.
- 相反, 如果没有
- main: js库项目的入口文件. 运行时工具如node, 打包工具如webpack, rollup都把其视为入口文件.
- module: js库项目的es module 格式入口文件. 打包工具如webpack, rollup都把其视为入口文件. 重要的是, 运行时工具如node不使用此字段.
- exports: node不使用
module
字段, 而是使用exports
字段.exports
字段可以同时指定commonjs和es module 格式的入口文件, 还可以指定入口文件的typescript 类型定义文件. 还可以指定其他导出点.
所以一个标准的js库项目的package.json
文件如下:
{
"type": "module"
"main": "dist/index.cjs", // 因为`type: module`, 所以以`.cjs`结尾才能指定commonjs格式的入口文件.
"module": "dist/index.js",
"exports": {
".": {
"import": "dist/index.mjs",
"require": "dist/index.js",
"types": "dist/index.d.ts"
}
}
}
可以看出, main
是早期使用的commonjs入口文件, 后来出现了es module 格式, 打包工具rollup设定了module
字段为es module 格式的入口文件, 用来缩减打包体积. 但是node不使用module
字段, 而是设定了exports
字段.
exports
字段完美覆盖了main
和module
字段. 但是为了兼容旧的软件, 建议在你的js库里保留main
和module
字段.
到底哪个文件被node执行
假如一个js库的package.json
文件如下:
{
"name": "library",
"main": "dist/index.js",
"module": "dist/index.es.js"
}
没有type:module
, 没有exports
. 当执行import library from 'library'
时, node会执行哪个文件?
你可能认为因为使用了es语法import
, 所以会执行"module": "dist/index.es.js"
. 但是实际上不是这样的.
因为node不使用module
字段, 这里也没有exports
字段. 所以node会执行"main": "dist/index.js"
文件. node会把main
字段的commonjs格式代码转换为es module再执行. node这样做是为了兼容旧的js库, 大量旧的js库只有main
字段指定的commonjs文件. 但是node不会把es module 格式的代码转换为commonjs格式.