包是一段能够复用的代码,这段代码能够从全局注册表下载到开发者的本地环境。每一个包可能会,也可能不会依赖于别的包。简单地说,包管理器是一段代码,它可让你管理依赖(你或者他人写的外部代码),你的项目须要这些依赖来正确运行。
为啥咱们须要一个包管理工具呢?由于咱们在Node.js
上开发时,会用到不少别人写的JavaScript
代码。若是咱们要使用别人写的某个包,每次都根据名称搜索一下官方网站,下载代码,解压,再使用,很是繁琐。node
更重要的是,若是咱们要使用模块A,而模块A又依赖于模块B,模块B又依赖于模块C和模块D,npm
能够根据依赖关系,把全部依赖的包都下载下来并管理起来。不然,靠咱们本身手动管理,确定既麻烦又容易出错。react
因而一个集中管理的工具应运而生:数据库
npm
官网上,若是要使用,直接经过npm
安装就能够直接用,不用管代码存在哪,应该从哪下载。Yarn
是为了弥补npm
的一些缺陷[速度慢,稳定性高]而出现的。”npm
为你和你的团队打开了链接整个JavaScript
天才世界的一扇大门。它是世界上最大的软件注册表,每星期大约有 30 亿次的下载量,包含超过 600000 个 包(package
) (即,代码模块)。来自各大洲的开源软件开发者使用npm
互相分享和借鉴。包的结构使您可以轻松跟踪依赖项和版本。
下面是关于 npm
的快速介绍:npm
由三个独立的部分组成:npm
package
)、设置参数以及管理 npm
使用体验的主要途径。registry
)package
)的信息。CLI
)CLI
经过命令行或终端运行。开发者经过 CLI 与 npm 打交道。Yarn
发布于2016年10月,并在Github
上迅速拥有了2.4万个Star。而npm
只有1.2万个star
。这个项目由一些高级开发人员维护,包括了Sebastian McKenzie
(Babel.js)和Yehuda Katz
(Ember.js、Rust、Bundler等)。json
Yarn
一开始的主要目标是解决上一节中描述的因为语义版本控制而致使的npm安装的不肯定性问题。虽然可使用npm shrinkwrap
来实现可预测的依赖关系树,但它并非默认选项,而是取决于全部的开发人员知道而且启用这个选项。
Yarn采起了不一样的作法。每一个yarn
安装都会生成一个相似于npm-shrinkwrap.json
的yarn.lock
文件,并且它是默认建立的。除了常规信息以外,yarn.lock
文件还包含要安装的内容的校验和,以确保使用的库的版本相同。
yarn的优化主要体如今:缓存
速度快 :安全
Yarn
有一个锁定文件 (lock file) 记录了被确切安装上的模块的版本号。每次只要新增了一个模块,Yarn 就会建立(或更新)yarn.lock
这个文件。这么作就保证了,每一次拉取同一个项目依赖时,使用的都是同样的模块版本。yarn
改变了一些npm
命令的名称,好比 yarn add/remove
,感受上比 npm 本来的 install/uninstall
要更清晰。执行工程自身 preinstall
网络
preinstall
钩子此时会被执行。肯定首层依赖react-router
dependencies
和 devDependencies
属性中直接指定的模块(假设此时没有添加 npm install
参数)。工程自己是整棵依赖树的根节点,每一个首层依赖模块都是根节点下面的一棵子树,npm 会开启多进程从每一个首层依赖模块开始逐步寻找更深层级的节点。获取模块dom
package.json
中每每是 semantic version
(semver,语义化版本)。此时若是版本描述文件(npm-shrinkwrap.json
或 package-lock.json
)中有该模块信息直接拿便可,若是没有则从仓库获取。如 packaeg.json
中某个包的版本是 ^1.1.0,npm
就会去仓库中获取符合 1.x.x
形式的最新版本。resolved
字段),npm 会用此地址检查本地缓存,缓存中有就直接拿,若是没有则从仓库下载。模块扁平化(dedupe
)
loadsh
,B 模块一样依赖于 lodash
。在 npm3 之前会严格按照依赖树的结构进行安装,所以会形成模块冗余。yarn
和从 npm5
开始默认加入了一个 dedupe
的过程。它会遍历全部节点,逐个将模块放在根节点下面,也就是 node-modules
的第一层。当发现有重复模块时,则将其丢弃。这里须要对重复模块进行一个定义,它指的是模块名相同且 semver
兼容。每一个 semver
都对应一段版本容许范围,若是两个模块的版本容许范围存在交集,那么就能够获得一个兼容版本,而没必要版本号彻底一致,这可使更多冗余模块在 dedupe
过程当中被去掉。安装模块
node_modules
,并执行模块中的生命周期函数(按照 preinstall、install、postinstall
的顺序)。执行工程自身生命周期
install、postinstall、prepublish、prepare
的顺序)。咱们要使用模块A,而模块A又依赖于模块B,模块B又依赖于模块C和模块D,npm能够根据依赖关系,把全部依赖的包都下载下来并管理起来,而这种依赖又有不同的表现形式。
假设目前工程依赖 A, B, C 三个库,而他们对某个库 somelib
存在这样的依赖关系:
A - somelib 1.4.x B - somelib 1.6.x C - somelib 1.6.x
若是要安装 ABC 三个库,那么 somelib
会存在版本冲突。npm5+/yarn
会在实际安装时,给三个库分别下载各自依赖的 somelib
版本。假设 npm 先安装了 A, 因为 A 依赖 somelib 1.4.x
版本,那么 自身依赖先安装1.4.x
。再安装 B, C 时,因为 B, C 依赖的都不是 1.4.x
, 因而安装完以后,关系就变成这个样子了:
node_modules ├── A │ └── node_modules │ └── somelib 1.4.x ├── B │ └── node_modules │ └── somelib 1.6.x └── C └── node_modules └── somelib 1.6.x
这样就是嵌套依赖。
很显然这种方式很大的浪费了磁盘空间。
当关联依赖中包括对某个软件包的重复引用,在实际安装时将尽可能避免重复的建立。
假设目前工程依赖 A, B, C 三个库,而他们对某个库 somelib
存在这样的依赖关系:
A - somelib 1.4.x B - somelib 1.6.x C - somelib 1.6.x
若是要安装 ABC 三个库,那么 somelib
会存在版本冲突。npm5+/yarn
会在实际安装时,给三个库分别下载各自依赖的 somelib
版本。假设 npm 先安装了 A, 因为 A 依赖 somelib 1.4.x
版本,那么 1.4.x
会变成主版本。再安装 B, C 时,因为 B, C 依赖的都不是 1.4.x
, 因而安装完以后,关系就变成这个样子了:
node_modules ├── A ├── somelib 1.4.x ├── B │ └── node_modules │ └── somelib 1.6.x └── C └── node_modules └── somelib 1.6.x
这样就是扁平依赖。
须要注意的是,明明 B, C 都依赖 1.6.x 版本,实际上
npm5+/yarn
却要把这个版本保存两次,这样明显是对磁盘空间的浪费。咱们把这种状况就称为不彻底扁平的。目前这种状况还没法安全解决。
锁文件是由包管理器自动生成的。它包含了重现所有的依赖源码树须要的全部信息、你的项目依赖中的全部信息,以及它们各自的版本。
如今值得强调的是,Yarn
使用了锁文件,而 npm5
之前没有默认锁文件,npm5
以后加入了默认锁文件功能。咱们会谈到这种差异致使的一些后果。既然我已经向你介绍了包管理器这部分,如今咱们来讨论依赖自己。
目前常见的两种lock文件:
packahe-lock.json
是npm5以后默认生成的锁文件yarn.lock
是yarn的锁文件{ "name": "package-name", "version": "1.0.0", "lockfileVersion": 1, "dependencies": { "cacache": { "version": "9.2.6", "resolved": "https://registry.npmjs.org/cacache/-/cacache-9.2.6.tgz", "integrity": "sha512-YK0Z5Np5t755edPL6gfdCeGxtU0rcW/DBhYhYVDckT+7AFkCCtedf2zru5NRbBLFk6e7Agi/RaqTOAfiaipUfg==" }, "duplexify": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.0.tgz", "integrity": "sha1-GqdzAC4VeEV+nZ1KULDMquvL1gQ=", "dependencies": { "end-of-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz", "integrity": "sha1-1FlucCc0qT5A6a+GQxnqvZn/Lw4=" }, } } }
能够看出来,package-lock.json把全部的包的依赖顺序列出来,第一次出现的包名会提高到顶层,后面重复出现的将会放入被依赖包的node_modules当中。引发不彻底扁平化问题。
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 package-1@^1.0.0: version "1.0.3" resolved "https://registry.npmjs.org/package-1/-/package-1-1.0.3.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" package-2@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/package-2/-/package-2-2.0.1.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" dependencies: package-4 "^4.0.0" package-3@^3.0.0: version "3.1.9" resolved "https://registry.npmjs.org/package-3/-/package-3-3.1.9.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" dependencies: package-4 "^4.5.0" package-4@^4.0.0, package-4@^4.5.0: version "4.6.3" resolved "https://registry.npmjs.org/package-4/-/package-4-2.6.3.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
显然yarn.lock锁文件把全部的依赖包都扁平化的展现了出来,对于同名包可是semver不兼容的做为不一样的字段放在了yarn.lock的同一级结构中。
在一个测试package工程里面,安装了如下三个包,安装了react-router 3.2.1,另外安装了react-router-dom 4.3.1 和react-router-native 4.3.0,这两个都依赖"react-router": "^4.3.0",结果以下:
.
└── node_modules
├── react-router-dom4.3.1 │ └── react-router4.3.1 ├── react-router-native4.3.0 │ └── react-router4.3.1 └── react-router3.2.1
查看package-lock.json结果和最后node_modules安装结果:
查看yarn.lock结果和最后node_modules安装结果: