npm、yarn、pnpm 区别
npm
npm 是 Node.js 能够如此成功的主要原因之一。
npm 是围绕着语义版本控制(semver)的思想而设计的,给定一个版本号:主版本号.次版本号.补丁版本号 , 以下这三种情况需要增加相应的版本号:
- 主版本号: 当 API 发生改变,并与之前的版本不兼容的时候;
- 次版本号: 当增加了功能,但是向后兼容的时候;
- 补丁版本号: 当做了向后兼容的缺陷修复的时候;
npm 使用一个名为 package.json 的文件,用户可以通过 npm install --save 命令把项目里所有的依赖项保存在这个文件里。
例如,运行 npm install --save lodash 会将以下几行添加到 package.json 文件中。
"dependencies": {
"lodash": "^4.17.4"
}
在版本号lodash之前有个^ 字符。这个字符告诉 npm,安装主版本等于 4 的任意一个版本即可。所以如果我现在运行 npm 进行安装,npm 将安装 lodash 的主版本为4的最新版,可能是 lodash@4.25.5(@是npm约定用来确定包名的指定版本的);
大多数npm库都严重依赖于其他 npm 库,这会导致嵌套依赖关系,并增加无法匹配相应版本的几率。
虽然可以通过 npm config set save-exact true 命令关闭在版本号前面使用^ 的默认行为,但这个只会影响顶级依赖关系。由于每个依赖的库都有自己的 package.json 文件,而在它们自己的依赖关系前面可能会有^ 符号,所以无法通过 package.json 文件为嵌套依赖的内容提供保证。
为了解决这个问题,npm 提供了 shrinkwrap 命令。此命令将生成一个 npm-shrinkwrap.json 文件,为所有库和所有嵌套依赖的库记录确切的版本。
然而,即使存在 npm-shrinkwrap.json 这个文件,npm 也只会锁定库的版本,而不是库的内容。即便 npm 现在也能阻止用户多次重复发布库的同一版本,但是 npm 管理员仍然具有强制更新某些库的权力。
npm 2会安装每一个包所依赖的所有依赖项。如果我们有这么一个项目,它依赖项目A,项目A依赖项目B,项目B依赖项目C,那么依赖树将如下所示:
node_modules
- package-A
-- node_modules
--- package-B
----- node_modules
------ package-C
-------- some-really-really-really-long-file-name-in-package-c.js
这个结构可能会很长。这对于基于 Unix 的操作系统来说只不过是一个小烦恼,但对于 Windows 来说却是个破坏性的东西,因为有很多程序无法处理超过260个字符的文件路径名。
npm 3 采用了扁平依赖关系树来解决这个问题,所以我们的3个项目结构现在看起来如下所示:
node_modules
- package-A
- package-B
- package-C
-- some-file-name-in-package-c.js
这样,一个原来很长的文件路径名就从./node_modules/package-A/node_modules/package-B/node-modules/some-file-name-in-package-c.js 变成了/node_modules/some-file-name-in-package-c.js 。
这种方法的缺点是,npm 必须首先遍历所有的项目依赖关系,然后再决定如何生成扁平的 node_modules 目录结构。npm 必须为所有使用到的模块构建一个完整的依赖关系树,这是一个耗时的操作,是 npm 安装速度慢的一个很重要的原因。
npm 是有本地缓存的,它保存了已经下载的每个版本的压缩包。本地缓存的内容可以通过 npm cache ls 命令进行查看。本地缓存的设计有助于减少安装时间。
yarn
yarn 一开始的主要目标是解决 npm 中描述的由于语义版本控制而导致的 npm 安装的不确定性问题。虽然可以使用 npm shrinkwrap 来实现可预测的依赖关系树,但它并不是默认选项,而是取决于所有的开发人员知道并且启用这个选项。
yarn 采取了不同的做法。每个 yarn 安装都会生成一个类似于 npm-shrinkwrap.json 的 yarn.lock 文件,而且它是默认创建的。除了常规信息之外,yarn.lock 文件还包含要安装的内容的校验和,以确保使用的库的版本相同。
由于 yarn 是崭新的经过重新设计的 npm 客户端,它能让开发人员并行化处理所有必须的操作,并添加了一些其他改进,这使得运行速度得到了显著的提升,整个安装时间也变得更少。
像 npm 一样,yarn 使用本地缓存。与 npm 不同的是,yarn 无需互联网连接就能安装本地缓存的依赖项,它提供了离线模式。
yarn 还提供了一些其他改进,例如,它允许合并项目中使用到的所有的包的许可证。
最简单的入门方法是运行:
npm install -g yarn
yarn
但 yarn 安装页面是这么说的:
注意:通常情况下不建议通过 npm 进行安装。npm 安装是非确定性的,程序包没有签名,并且 npm 除了做了基本的 SHA1 哈希之外不执行任何完整性检查,这给安装系统程序带来了安全风险。
pnpm
pnpm 运行起来非常的快,甚至超过了 npm 和 yarn。
因为它采用了一种巧妙的方法,利用硬链接和符号链接来避免复制所有本地缓存源文件,这是 yarn 的最大的性能弱点之一。
此外,截至2017年3月,它继承了yarn的所有优点,包括离线模式和确定性安装。
总结
yarn 是一个更安全的选择,但是 pnpm 可能是一些测试用例的更好的选择。例如,它可以在运行大量集成测试并希望尽可能快地安装依赖关系的中小型团队中发挥作用。
最后,npm仍然提供了一个非常有用的解决方案,支持大量的测试用例。大多数开发人员使用原始 npm 客户端仍然可以做得很好。
|