- yarn
- npm
- cnpm
他们三个究竟有什么区别?
npm
npm
是Node.js
能够如此成功的主要原因之一。npm
团队做了很多的工作,以确保npm
保持向后兼容,并在不同的环境中保持一致。npm
是围绕着 语义版本控制(semver
)的思想而设计。- 给定一个版本号:主版本号.次版本号.补丁版本号, 以下这三种情况需要增加相应的版本号:
- 主版本号: 当API发生改变,并与之前的版本不兼容的时候
- 次版本号: 当增加了功能,但是向后兼容的时候
- 补丁版本号:当做了向后兼容的缺陷修复的时候
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个项目结构现在看起来如下所示:
存了已经下载的每个版本的压缩包。本地缓存的内容可以通过 npm cache ls
命令进行查看。本地缓存的设计有助于减少安装时间。
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 install
命令时,NPM
都得从互联网上下载所有内容。
但是,npm
是有本地缓存的,它保存了已经下载的每个版本的压缩包。本地缓存的内容可以通过npm cache ls
命令进行查看。本地缓存的设计有助于减少安装时间。
cnpm
cnpm
跟npm
用法完全一致,只是在执行命令时将npm
改为cnpm
。
npm
安装插件是从国外服务器下载,受网络影响大,可能出现异常,如果npm
的服务器在中国就好了,于是淘宝团队干了这事。来自官网:“这是一个完整 npmjs.org 镜像,你可以用此代替官方版本(只读),同步频率目前为 10分钟 一次以保证尽量与官方服务同步。”
安装
npm install -g cnpm --registry=https://registry.npm.taobao.org
Yarn
Yarn
一开始的主要目标是解决上一节中描述的由于语义版本控制而导致的npm安装的不确定性问题。虽然可以使用npm shrinkwrap
来实现可预测的依赖关系树,但它并不是默认选项,而是取决于所有的开发人员知道并且启用这个选项。
Yarn
采取了不同的做法。每个yarn
安装都会生成一个类似于npm-shrinkwrap.json
的yarn.lock
文件,而且它是默认创建的。除了常规信息之外,yarn.lock
文件还包含要安装的内容的校验和,以确保使用的库的版本相同。
yarn
是经过重新设计的崭新的npm客户端
,它能让开发人员并行处理所有必须的操作,并添加了一些其他改进。
运行速度得到了显著的提升,整个安装时间也变得更少,像npm
一样,yarn
使用本地缓存。与npm
不同的是,yarn
无需互联网连接就能安装本地缓存的依赖项,它提供了离线模式。
允许合并项目中使用到的所有的包的许可证,通常情况下不建议通过npm
进行安装。npm
安装是非确定性的,程序包没有签名,并且npm
除了做了基本的SHA1哈希
之外不执行任何完整性检查,这给安装系统程序带来了安全风险。
首先我们从原理入手 ,我们使用
- npm init
- yarn init
- cnpm init
的时候 发生了什么 ?
- 生成
package.json
文件 json
文件内部声明初始的版本信息、作者信息等,如果你是需要上传到npm上作为命令行工具,应该配置bin等声明入口字段
那么当我们使用
- npm i
- yarn add
- cnpm i
操作时候会发生什么 ?
首先会根据你的命令行后缀是否加了 -g
或者global
判断,下载的包是放在全局的环境,还是当前package.json
文件对应的node_module
文件夹目录下(这点尤其重要,有人出BUG,就是因为在用npm
, cnpm
时候没有注明添加的是全局依赖还是本地依赖,导致json文件上没有对应的包名,项目永远起不来)
然后根据你的指令--save
或者-D
、--save -dev
判断是开发依赖还是线上依赖,其实这点在yarn
上没有问题,因为yarn
有自己的一套检查包完整性的机制,不会丢包,还会自动判断添加依赖,出bug
一般是cnpm
和npm
,没有明确-g
或者--save
,npm
只有检查程序员签名的机制,没有检查包完整性的机制,也不会自动添加依赖到json
文件,那么就会出现丢包的假象,所以建议主要使用yarn
「有时候,yarn 会出现 bug ,所以要看一下项目要求」
yarn和npm对比
npm | yarn |
---|---|
npm install | yarn |
npm install react –save | yarn add react |
npm uninstall react –save | yarn remove react |
npm install react –save-dev | yarn add react –dev |
npm update –save | yarn upgrade |
npm的缺点汇总
同一个项目,安装的时候无法保持一致性。由于package.json
文件中版本号的特点,下面三个版本号在安装的时候代表不同的含义。
"5.0.3",
"~5.0.3",
"^5.0.3"
“5.0.3”
表示安装指定的5.0.3
版本,“~5.0.3”
表示安装5.0.X
中最新的版本,“^5.0.3”
表示安装5.X.X
中最新的版本。这就麻烦了,常常会出现同一个项目,有的同事是OK的,有的同事会由于安装的版本不一致出现bug。
安装的时候,包会在同一时间下载和安装,中途某个时候,一个包抛出了一个错误,但是npm
会继续下载和安装包。因为npm
会把所有的日志输出到终端,有关错误包的错误信息就会在一大堆npm
打印的警告中丢失掉,并且你甚至永远不会注意到实际发生的错误。
yarn的优点
速度快 。速度快主要来自以下两个方面:
- 并行安装:无论
npm
还是Yarn
在执行包的安装时,都会执行一系列任务。npm
是按照队列执行每个package
,也就是说必须要等到当前package
安装完成之后,才能继续后面的安装。而Yarn
是并行执行所有任务,提高了性能。 - 离线模式:如果之前已经安装过一个软件包,用
Yarn
再次安装时之间从缓存中获取,就不用像npm
那样再从网络下载了。
安装版本统一:为了防止拉取到不同的版本,Yarn
有一个锁定文件 (lock file
) 记录了被确切安装上的模块的版本号。每次只要新增了一个模块,Yarn
就会创建(或更新)yarn.lock
这个文件。这么做就保证了,每一次拉取同一个项目依赖时,使用的都是一样的模块版本。npm
其实也有办法实现处处使用相同版本的 packages
,但需要开发者执行 npm shrinkwrap
命令。这个命令将会生成一个锁定文件,在执行 npm install
的时候,该锁定文件会先被读取,和 Yarn
读取yarn.lock
文件一个道理。npm
和 Yarn
两者的不同之处在于,Yarn
默认会生成这样的锁定文件,而 npm
要通过 shrinkwrap
命令生成 npm-shrinkwrap.json
文件,只有当这个文件存在的时候,packages
版本信息才会被记录和更新。
更简洁的输出:npm
的输出信息比较冗长。在执行 npm install <package>
的时候,命令行里会不断地打印出所有被安装上的依赖。相比之下,Yarn
简洁太多:默认情况下,结合了 emoji
直观且直接地打印出必要的信息,也提供了一些命令供开发者查询额外的安装信息。
多注册来源处理:所有的依赖包,不管他被不同的库间接关联引用多少次,安装这个包时,只会从一个注册来源去装,要么是 npm
要么是 bower
, 防止出现混乱不一致。
更好的语义化: yarn
改变了一些npm
命令的名称,比如 yarn add/remove
,感觉上比 npm
原本的 install/uninstall
要更清晰。
那不是标题党? 说了yarn
这么多优点,我们为什么还要npm
呢?
你不用 npm publish
,你怎么上传包到npm
? 你不用cnpm
,是下载不了一些包的
搞清楚三者下载包(依赖)的本质结果:
- 首先向对应的
node_module
文件夹下面下载包(如果你非要下到全局,那么再见) - 再然后是json文件中添加对应的依赖字段,确定是线上依赖还是开发依赖
- 只要做到这两者 你就下包成功
- 总结就是 只要结果 过程管你是啥
使用yarn
下载过的包,再使用npm
cnpm
下载 会重复下载,删除之前的包
puppeteer
这个包所依赖的mini
版谷歌浏览器使用cnpm
下载就可以完美解决prerender-spa-plugin
这个包依赖上面的木偶戏puppeteer
这个包,也可以用cnpm
下载- 混合使用包管理器切记,不要重复下载依赖,
npm
cnpm
下载依赖,一定要添加注明是什么依赖,是否全局安装 yarn
和npm i
两者,选择前者,原因在上面有提到,总体来说,yarn
是不二选择,但是其他两者也不可缺少,比如electron
这个依赖,使用cnpm
就可以完美一键安装