package.json
是配置和描述如何与程序交互和运行的中心。 npm CLI
(和 yarn
)用它来识别你的项目并了解如何处理项目的依赖关系。package.json
文件使 npm
可以启动你的项目、运行脚本、安装依赖项、发布到 NPM
注册表以及许多其他有用的任务。 npm CLI
也是管理 package.json
的最佳方法,因为它有助于在项目的整个生命周期内生成和更新package.json
文件。
package.json
会在项目的生命周期中扮演多个角色,其中某些角色仅适用于发布到 NPM
的软件包。即使你没有把项目发布到 NPM
注册表中,或者没有将其公开发布给其他人,package.json
对于开发流程仍然至关重要。
你的项目还必须包含package.json
,然后才能从 NPM
安装软件包(npm install xxx
)。
package.json
文件npm init
根据提示配置即可。
如果想直接使用默认值生成,可以添加 --yes
(-y
缩写)标志
npm init -y
从当前目录中提取的信息生成默认值。
直接在项目根目录下新建一个package.json
文件,按照json
格式编写对应字段即可。
主要是项目的基本信息,包括名称,版本,描述,仓库,作者等,部分会展示在 npm
官网上。
下图为npm
官网上的react
库展示:
如果打算把这个项目发布到npm
中,最重要的两个字段就是 name
和 version
,它们都是必须的,如果没有,就无法正常执行 npm install
命令。npm
规定 package.json
文件是由名称和版本号作为唯一标识符的。
包的名称。它在 URL
中使用,在命令行中作为参数,并作为项目中node_modules
目录下的目录名称。
https://registry.npmjs.org/[name]/-/[name]-[version].tgz
node_modules/[name]
npm install [name]
命名规范:
.
或_
开头。URL
的一部分、命令行上的参数和文件夹名称。因此,名称不能包含任何非 URL
安全字符。注意:如果要发布在npm
上,名称不能和其他模块的名称重复。在发布前可以通过npm view
命令查询模块名是否重复,如果不重复就会提示 404
,如果不打算发布在npm
上,name
字段不重要,不会影响项目的正常运行
npm view react
npm view qwdnioqwnigwudqhwdqw
提示404
项目版本号,开源项目的版本号通常遵循 semver 语义化规范。
可以通过以下命令来查看 npm
包的版本信息
// 查看最新版本号
npm view xxx version
// 查看所有版本
npm view xxx versions
可选字段,必须是字符串。当前包的描述信息,是一个字符串。它可以帮助开发者在使用npm
搜索时找到这个包。
如果 package.json
中没有 description
信息,npm
使用项目中的 README.md
的第一行作为描述信息。这个描述信息有助于别人搜索你的项目。
一个字符串数组,其作用与描述相似。 NPM
注册表会为该字段建立索引,能够在搜索软件包时帮助找到它们。数组中的每个值都是与程序包关联的一个关键字。
项目的仓库地址以及版本控制信息。
"repository": {
"type": "git",
"url": "https://github.com/xxx.git",
"directory": "xxx"
}
项目主页的链接,通常是项目 github
链接,项目官网或文档首页。
项目 bug
反馈地址,通常是 github issue
页面的链接。
"bugs": "https://xxxx/issues"
项目作者,有两种形式:
"author": "XXX (https://XXX)"
"author": {
"name" : "xxx",
"email" : "[email protected]",
"url" : "https://xxx"
}
email
和url
可选。
项目贡献者,数组类型。格式与author
类似
// 字符串格式
"contributors": [
"XXX (https://XXX)" ,
"XXX (https://XXX)"
]
// 对象格式
"contributors": [
{
"name" : "xxx",
"email" : "[email protected]",
"url" : "https://xxx"
},
{
"name" : "xxx",
"email" : "[email protected]",
"url" : "https://xxx"
}
]
赞助商
有两种方式:字符串 和 包含 type
和 url
的 对象 或者多个赞助商的数组。可以通过 npm fund
进行查看
{
{
"funding": {
"type" : "individual",
"url" : "http://example.com/donate"
},
"funding": {
"type" : "patreon",
"url" : "https://www.patreon.com/my-account"
},
"funding": "http://example.com/donate",
"funding": [
{
"type" : "individual",
"url" : "http://example.com/donate"
},
"http://example.com/donateAlso",
{
"type" : "patreon",
"url" : "https://www.patreon.com/my-account"
}
]
}
项目的开源许可证。项目的版权拥有人可以使用开源许可证来限制源码的使用、复制、修改和再发布等行为。
项目入口文件,通常是用于启动项目的文件。在 browser
和 Node
环境中都可以使用。如果我们将项目发布为 npm
包,那么当使用 require
导入 npm
包时,返回的就是 main
字段所列出的文件的 module.exports
属性。
默认值是模块根目录下面的index.js
比如packageA
的 main
字段指定为 index.js
,当通过import
或者require
引入packageA
时实际引入为node_modules/packageA/index.js
。
项目在进行 npm
发布时,可以通过 files
指定需要跟随一起发布的内容来控制 npm
包的大小,避免安装时间太长。
文件模式遵循与 .gitignore
类似的语法,无论如何设置都会始终包含和忽略某些文件
包含文件:
忽略文件:
通过files
字段可以指定更多需要一起发布的内容。可以是单独的文件,整个文件夹,或者使用通配符匹配到的文件。
"files": [
"xxx.js",
"xxx/",
"xxx/*.{js,css}"
]
值为'module'
则当作es
模块处理;
// 使用 ES 模块规范
node index.js
值为'commonjs'
则被当作commonJs
模块处理,如果没有定义则默认commonJs
规范处理。
当未设置type
时,js
文件按照commonjs
规范解析,import
方式会报错。
将type
设置成module
,就是使用ES
模块方式加载,require
方式会报错
不管type
定义什么值,.mjs
的文件都按照es
模块来处理,.cjs
的文件都按照commonJs
模块来处理。不过两种模块规范最好不要混用,会产生异常报错。
main
字段里指定的入口文件在 browser
和 Node
环境中都可以使用。如果只想在 web
端使用,不允许在 server
端使用,可以通过 browser
字段指定入口。
指定 ES
模块的入口文件。
"main": "./index.js",
"browser": "./browser/index.js",
"module": "./index.mjs"
node
在 14.13
支持在 package.json
里定义 exports
字段,拥有了条件导出的功能。
exports
字段可以配置不同环境对应的模块入口文件,并且当它存在时,它的优先级最高。
比如使用 require
和 import
字段根据模块规范分别定义入口:
"exports": {
"require": "./index.js",
"import": "./index.mjs"
}
}
这样的配置在使用 import 'xxx'
和 require('xxx')
时会从不同的入口引入文件,exports
也支持使用 browser
和 node
字段分别对应 browser
和 Node
环境中的入口。
上方的写法其实等同于:
"exports": {
".": {
"require": "./index.js",
"import": "./index.mjs"
}
}
}
为什么要加一个层级,把 require
和 import
放在 "."
下面呢?
因为 exports
除了支持配置包的默认导出,还支持配置包的子路径。
比如一些第三方 UI
包需要引入对应的样式文件才能正常使用。
import `packageA/dist/css/index.css`;
我们可以使用 exports
来封装文件路径:
"exports": {
"./style": "./dist/css/index.css'
},
用户引入时只需:
import `packageA/style`;
除了对导出的文件路径进行封装,exports
还限制了使用者访问未在 "exports"
中定义的任何其他路径。
比如发布的 dist
文件里有一些内部模块 dist/internal/module
,被用户单独引入使用的话可能会导致主模块不可用。为了限制外部的使用,我们可以不在 exports
定义这些模块的路径,这样外部引入 packageA/dist/internal/module
模块的话就会报错。
结合上面入口文件配置的知识,再来看看下方 vite 官网推荐的第三方库入口文件的定义,就很容易理解了。
项目的工作区配置,用于在本地的根目录下管理多个子项目。可以自动地在 npm install
时将 workspaces
下面的包,软链到根目录的 node_modules
中,不用手动执行 npm link
操作
workspaces
字段接收一个数组,数组里可以是文件夹名称或者通配符。比如:
"workspaces": [
"workspace-a"
]
表示在 workspace-a
目录下还有一个项目,它也有自己的 package.json
。
package.json
workspace-a
└── package.json
通常子项目都会平铺管理在 packages
目录下,所以根目录下 workspaces
通常配置为:
"workspaces": [
"packages/*"
]
指定项目的一些内置脚本命令,这些命令可以通过 npm run
来执行。
"script": {
"start": "node index.js"
}
可以使用npm run start
或者 yarn start
运行,当执行npm run start
的时候,自动创建了一个Shell
,在这个Shell
里面执行指定node index.js
命令。
config
用于设置 scripts
里的脚本在运行时的参数。比如设置 port
为 8080
"config": {
"port": "8080",
"xxx": "xxxx"
}
可以在执行脚本中通过process.env.npm_package_config_port
(npm_package_config_xxx
)进行访问。
项目可能会依赖其他包,需要在 package.json
里配置这些依赖的信息。
运行依赖,也就是项目生产环境下需要用到的依赖。比如 react
,vue
,状态管理库以及组件库等。
使用 npm install xxx
或则 npm install xxx --save
时,会被自动插入到该字段中。
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
开发依赖,项目开发环境需要用到而运行时不需要的依赖,用于辅助开发,通常包括项目工程化工具比如 webpack
,vite
,eslint
等。
使用 npm install xxx -D
或者 npm install xxx --save-dev
时,会被自动插入到该字段中。
"devDependencies": {
"webpack": "^5.69.0"
}
同伴依赖,一种特殊的依赖,不会被自动安装,通常用于表示与另一个包的依赖与兼容性关系来警示使用者。
比如我们安装 A,A 的正常使用依赖 [email protected] 版本,那么 [email protected] 就应该被列在 A 的 peerDependencies 下,表示“如果你使用我,那么你也需要安装 B,并且至少是 2.x 版本”。
比如 React 组件库 Ant Design,它的 package.json 里 peerDependencies 为
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
}
表示如果你使用 Ant Design
,那么你的项目也应该安装 react
和 react-dom
,并且版本需要大于等于 16.9.0。
可选依赖,顾名思义,表示依赖是可选的,它不会阻塞主功能的使用,安装或者引入失败也无妨。这类依赖如果安装失败,那么 npm
的整个安装过程也是成功的。
比如我们使用 colors
这个包来对 console.log
打印的信息进行着色来增强和区分提示,但它并不是必需的,所以可以将其加入到 optionalDependencies
,并且在运行时处理引入失败的逻辑。
使用 npm install xxx -O
或者 npm install xxx --save-optional
时,依赖会被自动插入到该字段中。
“optionalDependencies”: {
“colors”: “^1.4.0”
}
同伴依赖也可以使用 peerDependenciesMeta 将其指定为可选的。
“peerDependencies”: {
“colors”: “^1.4.0”
},
“peerDependenciesMeta”: {
“colors”: {
“optional”: true
}
}
打包依赖。它的值是一个数组,在发布包时,bundleDependencies 里面的依赖都会被一起打包。
比如指定 react
和 react-dom
为打包依赖:
“bundleDependencies”: [
“react”,
“react-dom”
]
在执行 npm pack
打包生成tgz
压缩包中,将出现 node_modules
并包含 react
和 react-dom
。
需要注意的是,这个字段数组中的值必须是在 dependencies
,devDependencies
两个里面声明过的依赖才行。
普通依赖通常从 npm registry
安装,但当你想用一个不在 npm registry
里的包,或者一个被修改过的第三方包时,打包依赖会比普通依赖更好用。
overrides
可以重写项目依赖的依赖,及其依赖树下某个依赖的版本号,进行包的替换。
比如某个依赖 A,由于一些原因它依赖的包 [email protected]
需要替换,我们可以使用 overrides
修改 foo
的版本号:
“overrides”: {
“foo”: “1.1.0-patch”
}
当然这样会更改整个依赖树里的 foo
,我们可以只对 A
下的 foo
进行版本号重写:
“overrides”: {
“A”: {
“foo”: “1.1.0-patch”,
}
}
overrides
支持任意深度的嵌套。
如果在 yarn
里也想复写依赖版本号,需要使用 resolution
字段,而在 pnpm
里复写版本号需要使用 pnpm.overrides
字段。
主要是和项目发布相关的配置。
如果是私有项目,不希望发布到公共 npm
仓库上,可以将 private
设为 true
。
npm
包发布时使用的配置。
比如在安装依赖时指定了 registry
为 taobao
镜像源,但发布时希望在公网发布,就可以指定 publishConfig.registry
。
"publishConfig": {
"registry": "https://registry.npmjs.org/"
}
和项目关联的系统配置,比如 node
版本或操作系统兼容性之类。这些要求只会起到提示警告的作用,即使用户的环境不符合要求,也不影响安装依赖包。
一些项目由于兼容性问题会对 node
或者包管理器有特定的版本号要求,比如:
"engines": {
"node": ">=14 <16",
"pnpm": ">7"
}
要求 node
版本大于等于 14 且小于 16,同时 pnpm
版本号需要大于 7。
在 linux
上能正常运行的项目可能在 windows
上会出现异常,使用 os
字段可以指定项目对操作系统的兼容性要求。
"os": ["darwin", "linux"]
在操作系统前加一个!
可以禁止该系统运行项目
"os": [!"win32"]
指定项目只能在特定的 CPU
体系上运行。
"cpu": ["x64", "ia32"]
跟os
一样加!
也可以禁止某CPU
"cpu": ["!arm"]