package.json
中有非常多的属性,其中必须填写的只有两个:name
和 version
,这两个属性组成一个 npm 模块的唯一标识。
{
"description": "An enterprise-class UI design language and React components implementation",
"keywords": [
"ant",
"component",
"components",
"design",
"framework",
"frontend",
"react",
"react-component",
"ui"
]
}
description用于添加模块的的描述信息,方便别人了解你的模块。
keywords用于给你的模块添加关键字。
当然,他们的还有一个非常重要的作用,就是利于模块检索。
描述开发人员的字段有两个:author 和 contributors
homepage 用于指定该模块的主页;
repository 用于指定模块的代码仓库;
我们的项目可能依赖一个或多个外部依赖包,根据依赖包的不同用途,我们将他们配置在下面几个属性下:dependencies
、devDependencies
、peerDependencies
、bundledDependencies
、optionalDependencies
。
dependencies
指定了项目运行所依赖的模块,开发环境和生产环境的依赖模块都可以配置到这里,例如:
"dependencies": {
"lodash": "^4.17.13",
"moment": "^2.24.0",
}
devDependencies
:有一些包有可能你只是在开发环境中用到,例如你用于检测代码规范的 eslint
,用于进行测试的 jest
,这些依赖照样会在你本地进行 npm install
时被安装和管理,但是不会被安装到生产环境。
"devDependencies": {
"jest": "^24.3.1",
"eslint": "^6.1.0",
}
peerDependencies
用于指定你正在开发的模块所依赖的版本以及用户安装的依赖包版本的兼容性。
"peerDependencies": {
"react": ">=16.0.0",
"react-dom": ">=16.0.0"
}
optionalDependencies
:依赖包可能不是强依赖的,这个依赖包的功能可有可无,当这个依赖包无法被获取到时,你希望 npm install
继续运行,而不会导致失败,你可以将这个依赖放到 optionalDependencies
中,注意 optionalDependencies
中的配置将会覆盖掉 dependencies
所以只需在一个地方进行配置。
bundledDependencies
:和以上几个不同,bundledDependencies
的值是一个数组,数组里可以指定一些模块,这些模块将在这个包发布时被一起打包。
"bundledDependencies": ["package1" , "package2"]
license
字段用于指定软件的开源协议,开源协议里面详尽表述了其他人获得你代码后拥有的权利,可以对你的的代码进行何种操作,何种操作又是被禁止的。
{
"main": "lib/index.js",
}
main
属性可以指定程序的主入口文件
当你的模块是一个命令行工具时,你需要为命令行工具指定一个入口,即指定你的命令名称和本地可指定文件的对应关系。
{
"bin": {
"chenghuai": "./bin/index.js"
}
}
{
"files": [
"dist",
"lib",
"es"
]
}
man 命令是 Linux 下的帮助指令,通过 man 指令可以查看 Linux 中的指令帮助、配置文件帮助和编程帮助等信息。
如果你的 node.js 模块是一个全局的命令行工具,在 package.json 通过 man 属性可以指定 man 命令查找的文档地址。
{
"man" : [
"/Users/isaacs/dev/npm/cli/man/man1/npm-access.1",
"/Users/isaacs/dev/npm/cli/man/man1/npm-audit.1"
]
}
一个 node.js 模块是基于 CommonJS 模块化规范实现的,严格按照 CommonJS 规范,模块目录下除了必须包含包描述文件 package.json 以外,还需要包含以下目录:
{
"directories": {
"lib": "src/lib/",
"bin": "src/bin/",
"man": "src/man/",
"doc": "src/doc/",
"example": "src/example/"
}
}
{
"scripts": {
"test": "jest --config .jest.js --no-cache",
"dist": "antd-tools run dist",
"compile": "antd-tools run compile",
"build": "npm run compile && npm run dist"
}
}
scripts 用于配置一些脚本命令的缩写,各个脚本可以互相组合使用,这些脚本可以覆盖整个项目的生命周期,配置后可使用 npm run command
进行调用。如果是 npm 关键字,则可以直接调用。例如,上面的配置制定了以下几个命令:npm run test
、npm run dist
、npm run compile
、npm run build
。
config
字段用于配置脚本中使用的环境变量,例如下面的配置,可以在脚本中使用process.env.npm_package_config_port
进行获取。
{
"config" : { "port" : "8080" }
}
如果你的 node.js 模块主要用于安装到全局的命令行工具,那么该值设置为 true ,当用户将该模块安装到本地时,将得到一个警告。这个配置并不会阻止用户安装,而是会提示用户防止错误使用而引发一些问题。
如果将 private 属性设置为 true,npm将拒绝发布它,这是为了防止一个私有模块被无意间发布出去。
"publishConfig": {
"registry": "https://registry.npmjs.org/"
},
发布模块时更详细的配置,例如你可以配置只发布某个 tag、配置发布到的私有 npm 源。
假如你开发了一个模块,只能跑在 linux 系统下,你需要保证 windows 用户不会安装到你的模块,从而避免发生不必要的错误。
使用 os 属性可以帮助你完成以上的需求,你可以指定你的模块只能被安装在某些系统下,或者指定一个不能安装的系统黑名单:
"os" : [ "darwin", "linux" ]
"os" : [ "!win32" ]
在node环境下可以使用 process.platform 来判断操作系统。
和上面的 os 类似,我们可以用 cpu 属性更精准的限制用户安装环境:
"cpu" : [ "x64", "ia32" ]
"cpu" : [ "!arm", "!mips" ]
在node环境下可以使用 process.arch 来判断 cpu 架构。
你可以执行 npm view package version
查看某个 package 的最新版本。
执行npm view encode-fe-lint versions
查看某个 package 在npm服务器上所有发布过的版本。
npm包 中的模块版本都需要遵循 SemVer规范
——由 Github 起草的一个具有指导意义的,统一的版本号表示规则。实际上就是 Semantic Version
(语义化版本)的缩写。
SemVer规范官网: semver.org/
SemVer规范的标准版本号采用 X.Y.Z 的格式,其中 X、Y 和 Z 为非负的整数,且禁止在数字前方补零。X 是主版本号、Y 是次版本号、而 Z 为修订号。每个元素必须以数值来递增。
在开发中肯定少不了对一些版本号的操作,如果这些版本号符合 SemVer
规范 ,我们可以借助用于操作版本的npm包semver来帮助我们进行比较版本大小、提取版本信息等操作。
Npm 也使用了该工具来处理版本相关的工作。
npm install semver
semver.gt('1.2.3', '9.8.7') // false
semver.valid('1.2.3') // '1.2.3'
semver.valid(semver.coerce('v2')) // '2.0.0'
semver.clean(' =v1.2.3 ') // '1.2.3'
我们经常看到,在 package.json 中各种依赖的不同写法:
在 package.json 文件中最常见的应该是 “yargs”: “^14.0.0” 这种格式的 依赖, 因为我们在使用 npm install package 安装包时,npm 默认安装当前最新版本,然后在所安装的版本号前加 ^ 号。
注意,当主版本号为 0 的情况,会被认为是一个不稳定版本,情况与上面不同:
lock文件
实际开发中,经常会因为各种依赖不一致而产生奇怪的问题,或者在某些场景下,我们不希望依赖被更新,建议在开发中使用 package-lock.json。
定期更新依赖
我们的目的是保证团队中使用的依赖一致或者稳定,而不是永远不去更新这些依赖。
使用 npm outdated
可以帮助我们列出有哪些还没有升级到最新版本的依赖:
npm update
会升级所有的红色依赖。Yarn
,或 Yet Another Resource Navigator,是 Facebook 开发的一个相对较新的包管理器。它的开发是为了提供 NPM 当时缺乏的更高级的功能(例如版本锁定,但后续的功能npm都补充上了),同时也使其更安全、更可靠和更高效。
Yarn 现在更像是 NPM 的替代品,由于 Yarn 没有预装 Node.js,因此需要显式安装:
npm install yarn -g
全局安装后,您可以通过在项目中设置版本来在每个项目的基础上使用它,如下所示:
yarn set version <version-name>
yarn的特点:
node_modules
文件夹。相反,它会生成一个映射项目依赖关系的 .pnp.cjs
文件。这会导致更优化的依赖树和更快的项目启动和包安装;.pnp.cjs
文件来映射离线缓存中的包。这使您可以快速检索和安装已保存的软件包;
Yarn 也支持 NPM 创建的 package-lock.json 文件,方便将版本数据从 NPM 迁移到 Yarn
下图显示了 NPM 和 Yarn 在各种情况下安装依赖项所用时间的比较:
使用 NPM,您还可以执行手动审核以查找任何漏洞并解决它。要查找漏洞,您可以使用 npm audit
虽然 Yarn 比 NPM是NPM后出来的,但它似乎正在迅速普及。
Yarn
优点
总结:NPM 更受习惯于旧版本工作流程并对当前工作流程感到满意的开发人员的青睐。它提供了不错的用户体验,同时还节省了硬盘空间。另一方面,Yarn 具有高级功能,例如即插即用和零安装,可以略微提高性能和安全性,但会以硬盘空间为代价。
pnpm,即performant npm,高性能的npm。相比起目前主流的包管理器,pnpm是速度快、节省磁盘空间的包管理器。
可以看出,与目前主流的包管理器npm、yarn相比,无论有无cache、有无lockfile、有无node_modules,pnpm的安装速度都有明显的优势。
为什么pnpm这么快呢?最主要的原因是,pnpm通过hard link
(硬连接)机制,节省磁盘空间并提升安装速度。
当使用 npm 或 Yarn 时,如果你有 100 个项目, 并且所有项目都有一个相同的依赖包,那么, 你在 硬盘上就需要保存 100 份该相同package(依赖包)的副本。
而使用 pnpm,package将被存放在一个统一的位置(如 Mac是 ~/.pnpm-store )。当安装软件包时,其包含的所有文件都会硬链接自此位置,而不会占用额外的硬盘空间。
除了节省磁盘空间,pnpm还有另一个重要特点是,建立非扁平的node_modules目录,并在引用依赖的时候通过sybolic link
机制找到对应.pnpm目录的地址。
使用pnpm安装依赖,打开node_modules,我们可以看到,node_modules目录是非扁平的(与npm@2一样),同时还多了一个.pnpm的目录。而node_modules下的依赖包都是sybolic link
(软链接:类似于windows系统中的快捷方式)
这样,pnpm 实现了相同模块不同版本之间隔离和复用。而且,node_modules目录是非扁平的,不存在由于提升带来的幽灵依赖问题。
hard link
在全局里面搞个 store
目录来存储 node_modules
依赖里面的 hard link 地址。这样节省了磁盘空间,并提升安装速度;sybolic link
去找到对应虚拟磁盘目录下(.pnpm 目录)的依赖地址。这样,避免了之前npm和yarn扁平/非扁平安装带来的一系列问题;