npm & yarn 包管理机制
1. npm
npm 会优先将依赖包安装到项目目录。
这样做的好处是使不同项目的依赖独立开来;缺点也比较明显,如果我们两个项目 repo_a 和 repo_b 都有一个相同的依赖 pkg_c,那么这个公共依赖将在两个项目中各被安装一次。也就是说,同一个依赖可能在我们的电脑上多次安装。
npm install
上图是 npm 安装依赖大致的过程,其中这样几个步骤需要关注:
检查配置
包括项目级、用户级、全局级、内置的 .npmrc 文件。
确定依赖版本,构建依赖树。
确定项目依赖版本有两个来源,一是 package.json 文件,一是 lockfile 文件,两个确认版本、构建依赖树的来源,互不可少、相辅相成。
如果 package-lock.json 文件存在且符合 package.json 声明的的情况下,直接读取;否则重新确认依赖的版本。
下载包资源
下载前先确认本地是否存在匹配的缓存版本,如果有就直接使用缓存文件,如果没有就下载并添加到缓存,然后将包按依赖树解压到 node_modules 目录。
生成 lockfile 文件
2. yarn
yarn 作为区别于 npm 的依赖管理工具,诞生之初就是为了解决历史上 npm 的某些不足,比如 npm 缺乏对于依赖的完整性和一致性保障,以及 npm 安装速度过慢的问题等,yarn 提出的安装理念很好的解决了当时 npm 的依赖管理问题:
yarn 特性
确定性
通过 yarn.lock 等机制,保证了确定性,这里的确定性包括但不限于明确的依赖版本、明确的依赖安装结构等。即在任何机器和环境下,都可以以相同的方式被安装。
模块扁平化安装
将依赖包的不同版本,按照一定策略,归结为单个版本,以避免创建多个副本造成冗余。(npm 也有相同的优化)
更好的网络性能。
Yarn 采用了请求排队的理念,类似并发连接池,能够更好地利用网络资源;同时引入了更好的安装失败时的重试机制。(npm 较早的版本是顺序下载,当第一个包完全下载完成后,才会将下载控制权交给下一个包)
引入缓存机制,实现离线策略。(npm 也有类似的优化)
yarn install
以下是在 yarn 安装依赖时的步骤:
检测包
这一步主要检测项目中是否存在npm相关的文件,如 package-lock.json 如果存在,则给出提示,可能会引发冲突,同时在这一步会去解析OS、CPU等相关信息。
解析包 resolving packages
这一步重点就是分析依赖,得到一个依赖关系图,确定版本信息等。
首先获取项目 package.json 中声明的首层依赖,包括 dependencies, devDependencies, optionalDependencies 声明的依赖。
接着采用遍历首层依赖的方式获取依赖包的版本信息,以及递归查找每个依赖下嵌套依赖的版本信息,并将解析过和正在解析的包用一个 Set 数据结构来存储,这样就能保证同一个版本范围内的包不会被重复解析。
对于没有解析过的包,首次尝试从 yarn.lock 中获取到版本信息,并标记为已解析;如果在 yarn.lock 中没有找到包,则向 Registry 发起请求获取满足版本范围的已知最高版本的包信息,获取后将当前包标记为已解析。
总之,在经过复杂的解析算法后,我们就确定了所有依赖的具体版本信息以及下载地址。
获取包(fetching packages)
这一步主要是到缓存中找到具体的包资源。首先会尝试在缓存中查找依赖包,如果没有命中缓存,则将依赖包下载到缓存中。
对于没有命中缓存的包,Yarn 会维护一个 fetch 队列,按照规则进行网络请求。这里也是 yarn 诞生之初解决 npm v3 安装缓慢问题的优化点,支持并行下载。
如何判断有没有命中缓存?
判断系统中存在符合 "cachefolder+slug+node_modules+pkg.name" 规则的路径,如果存在则判断为命中缓存,否则就会重新下载。
链接包(linking dependencies)
这一步主要是将缓存中的依赖,复制到项目目录下,同时遵循扁平化原则。
在复制依赖前,Yarn 会先解析 peerDependencies,如果找不到符合 peerDependencies 声明的依赖版本,则进行 warning 提示(这并不会影响命令执行),并最终拷贝依赖到项目中。
构建包(building fresh package)
如果依赖包中存在二进制包需要进行编译,会在这一步进行。
共 0 条评论