npm,yarn,cnpm,pnpm的介绍
npm
首先说一下Node.js,简单的说 Node.js 就是运行在服务端的 JavaScript。node通过更改连接到服务器的方式,可以处理高并发任务。
npm: nodejs的包管理器,用于node插件管理(包括安装、卸载、管理依赖等)
npm使用一个名为package.json
的文件,用户可以通过npm install --save
命令把项目里所有的依赖项保存在这个文件里。
npm安装速度慢的一个重要原因:npm远程服务器在国外,必须首先遍历所有的项目依赖关系,然后再决定如何生成扁平化的node_modules结构。不过,npm有本地缓存,它保存了已经下载的每个版本的压缩包,缓存可以减少安装时间。
安装单个npm包:
devDependencies:通常是开发的工具(eg:测试用的库),开发环境的依赖
dependencies:与生产环境中应用程序相关的
两者的区别在普通项目中,两者都安装,程序才能跑起来(因为devDependencies一般包含程序运行环境依赖);纯node项目,只安装dependencies就可以运行项目了,而安装了 devDependencies
后,就会让编辑器的 eslint
检测插件开始工作,或者是可以用 jest
进行单元测试:
是真正意义上的开发环境与生产环境分离。
npm使用方法:
第一种:
直接安装cnpm 安装淘宝提供的cnpm,并更改服务器地址为淘宝的国内地址, 命令:
npm install -g cnpm --registry=https://registry.npm.taobao.org
,以后安装直接采用cpm
替代npm
, 例如原生npm命令为:npm install uniq --save
,cnpm命令为:cnpm install uniq --save
第二种:
替换npm仓库地址为淘宝镜像地址(推荐) 命令:npm config set registry https://registry.npm.taobao.org
, 查看是否更改成功:npm config get registry
,以后安装时,依然用npm命令,但是实际是从淘宝国内服务器下载的
yarn
安装 | Yarn 中文文档 (bootcss.com)
快速、可靠、安全的依赖管理工具。
一开始是为了解决由于语义版本控制而导致npm安装的不确定的问题,每次安装都生成yarn.lock
文件,yarn.lock
文件还包含要安装的内容的校验和,以确保使用的库的版本相同。
yarn的改进:
运行速度得到了显著的提升
像npm一样,yarn使用本地缓存。与npm不同的是,yarn无需互联网连接就能安装本地缓存的依赖项,它提供了离线模式
yarn的workspace
Yarn Workspaces(工作区)是Yarn提供的monorepo
的依赖管理机制,从Yarn 1.0开始默认支持,用于在代码仓库的根目录下管理多个package的依赖。
Workspace 能更好的统一管理有多个项目的仓库,既可在每个项目下使用独立的 package.json 管理依赖,又可便利的享受一条 yarn命令安装或者升级所有依赖等。更重要的是可以使多个项目共享同一个 node_modules
目录,提升开发效率和降低磁盘空间占用。
pnpm
Fast, disk space efficient package manager | pnpm官网
全称是 “Performant NPM”,即高性能的 npm。利用硬链接和符号链接来避免复制本地缓存源文件,继承了yarn的所有优点,包括离线模式和确定性安装
特点:速度快,节约磁盘空间,安全性高
高效利用磁盘空间
内部基于内容寻址的文件系统存储磁盘上所有的文件:
这个文件好处在于:
1.不会重复安装同一个包。设想一下,假如公司的每个项目都用了 vue 全家桶,那么每个项目都需要安装 vue、vue-router、vuex、axios 等几乎相同的库,如果有 100 个项目,那就要重复安装 100 遍!!非常耗费磁盘空间。
2.即使包的不同版本,pnpm也会极大程度的复用之前版本的代码。举个例子,比如 lodash 有 100 个文件,更新版本之后多了一个文件,那么磁盘当中并不会重新写入 101 个文件,而是保留原来的 100 个文件的 hardlink
,仅仅写入那一个新增的文件
。
支持 monorepo
对于多个项目的管理,一般用多个git仓库。但是monorepo的宗旨就是用一个git仓库管理多个子项目,所有子项目都存放在根目录的packages
目录下,那么一个子项目就代表一个package
如果你之前没接触过 monorepo 的概念,建议仔细看看这篇文章以及开源的 monorepo 管理工具lerna,项目目录结构可以参考一下 babel 仓库。
pnpm还保留了非扁平化的node_modules文件夹,避免了扁平化node_modules都带来的幻影依赖,依赖分身的问题。
安装可以使用npm安装:npm i -g pnpm
pnpm 的 node_modules
与依赖提升和扁平化的 node_modules 不同,pnpm 引入了另一套依赖管理策略:内容寻址存储。
cnpm
与npm用法完全一致
npm安装插件是从国外服务器下载,受网络影响大,可能出现异常。淘宝为我们搭建了一个国内的npm服务器cnpm,它目前是每隔10分钟将国外npm仓库的所有内容“搬运”回国内的服务器上,这样我们直接访问淘宝的国内服务器就可以了.
-
cnpm 比 npm 快多了
-
cnpm 没有 package-lock.json
cnpm
最大的隐患:安装时不会产生 package-lock.json
,并且项目中即使有 package-lock.json
,cnpm
也是不管不顾的,只读取 package.json
。阿hi有一个因哈UN就是用的包不一定遵循Semver
规范。
- 有些依赖包用不了
有些依赖包用 cnpm 安装就不能用,用 npm 安装就可以用,这个问题估计和 cnpm
包的使用软链接的方式有关系(并不确定)。
cnpm 和 npm 混用,导致包挂了,这个可以确定是 cnpm
使用软链接的问题。所以,还是尽量不要混用吧。
能用 npm 最好用 npm,公司内部的 私有镜像源
也建议做成 npm
,毕竟 cnpm
还是存在一些隐患。
扁平化的node_modules
node_modules
├── A
│ └── node_modules
│ └── foo
└── B└── node_modules└── foo
最初,npm就简单通过依赖去递归安装包,A,B都依赖foo,那么就会有两份foo安装。为节省空间,采用了扁平化的node_modules。这样,foo会被提升到顶层,同一个包只会有一份。
node_modules
├── A
├── B
└── foo
但是会带来的问题是引入混乱,扁平化结构可以直接引用foo,但实际上并没有直接指定依赖foo,而是有层级关系,导致了引入上的错乱。如果A,B都不依赖foo,包会报错。
导致的问题:
幻影依赖:项目代码引用的某个包没有直接定义在 package.json
中,而是作为子依赖被某个包顺带安装了。代码里依赖幻影依赖的最大隐患是,对包的语义化控制不能穿透到其子包,也就是包 a@patch
的改动可能意味着其子依赖包 b@major
级别的 Break Change。(也就是说如果包改变了,那么被依赖的包就会破碎,出错)
但还有一种更难以解决的幻影依赖问题,即用户在 Monorepo 项目根目录安装了某个包,这个包可能被某个子 Package 内的代码寻址到,要彻底解决这个问题,需要配合使用 Rush,在工程上通过依赖问题检测来彻底解决。
对比其优劣势
- npm
- node_modules扁平化结构
- 支持workspace,大部分开发人员用npm也能很好的打包
- 逐行安装(速度较慢)
- 问题:幻影依赖,依赖分身
- yarn
- node_modules扁平化解构
- 支持workspace,monorepo
- yarn主要为了解决语义版本控制而导致npm安装不确定的问题,由yarn.lock文件,不过npm也出了package-lock文件
- 并行安装(速度较快)
- 问题:幻影依赖,依赖分身
- pnpm
- pnpm采用内存寻址存储的方法,通过软硬链接引用依赖,实现node_modules非扁平化结构,
- 支持workspace,monorepo,速度快,磁盘占用空间小(速度最快)
- 通过内容寻址存储解决了幻影依赖,依赖分身的问题,安全性高。
- pnpm 的安装速度在大多数场景都比 npm 和 yarn 快 2 倍,节省的磁盘空间也更多。
- 问题:
- 因为依赖源文件是安装在 store 中,调试依赖或 patch-package 给依赖打补丁也不太方便,可能会影响其他项目。
- 由于 pnpm 创建的 node_modules 依赖软链接,因此在不支持软链接的环境中,无法使用 pnpm,比如 Electron 应用。