0%

npm,yarn,pnpm区别

npm:

嵌套结构

我们都知道,执行 npm install 后,依赖包被安装到了 node_modules ,下面我们来具体了解下,npm 将依赖包安装到 node_modules 的具体机制是什么。

npm 的早期版本, npm 处理依赖的方式简单粗暴,以递归的形式,严格按照 package.json 结构以及子依赖包的 package.json 结构将依赖安装到他们各自的 node_modules 中。直到有子依赖包不在依赖其他模块。

扁平结构

为了解决以上问题,NPM3.x 版本做了一次较大更新。其将早期的嵌套结构改为扁平结构:

  • 安装模块时,不管其是直接依赖还是子依赖的依赖,优先将其安装在 node_modules 根目录。
  • npm 3.x 版本并未完全解决老版本的模块冗余问题,加入你的项目没有依赖B,但是依赖了不同B版本的A@1和A@2,由于在执行npm install,按照package.json依赖的顺序依次解析,则A@1和A@2在package.json的位置决定了node_modules的依赖结构,可能先依赖A@1也可能先依赖A@2,造成了不确定性。

Lock文件

为了解决 npm install 的不确定性问题,在 npm 5.x 版本新增了 package-lock.json 文件,而安装方式还沿用了 npm 3.x 的扁平化的方式。

package-lock.json 的作用是锁定依赖结构,即只要你目录下有 package-lock.json 文件,那么你每次执行 npm install 后生成的 node_modules 目录结构一定是完全相同的。

整体流程

好了,我们再来整体总结下上面的流程:

  • 检查 .npmrc 文件:优先级为:项目级的 .npmrc 文件 > 用户级的 .npmrc 文件> 全局级的 .npmrc 文件 > npm 内置的 .npmrc 文件

  • 检查项目中有无 lock 文件。

  • 1
    lock

    文件:

    • npm 远程仓库获取包信息

    • 根据

      1
      package.json

      构建依赖树,构建过程:

      • 构建依赖树时,不管其是直接依赖还是子依赖的依赖,优先将其放置在 node_modules 根目录。
      • 当遇到相同模块时,判断已放置在依赖树的模块版本是否符合新模块的版本范围,如果符合则跳过,不符合则在当前模块的 node_modules 下放置该模块。
      • 注意这一步只是确定逻辑上的依赖树,并非真正的安装,后面会根据这个依赖结构去下载或拿到缓存中的依赖包
    • 在缓存中依次查找依赖树中的每个包

      • 不存在缓存:
        • npm 远程仓库下载包
        • 校验包的完整性
        • 校验不通过:
          • 重新下载
        • 校验通过:
          • 将下载的包复制到 npm 缓存目录
          • 将下载的包按照依赖结构解压到 node_modules

      存在缓存:将缓存按照依赖结构解压到 node_modules

    • 将包解压到 node_modules

    • 生成 lock 文件

pnpm

pnpm好处:

  • 安装速度快(非扁平的包结构,没有复杂的扁平算法,只更新变化的文件)

  • 节省磁盘空间,统一安装到磁盘的某个位置,项目中的node_modules通过hard-link的方式链接到实际安装地址

  • pnpm 与 npm/yarn 另外一个很大的不同就是支持了 monorepo,体现在各个子命令的功能上,比如在根目录下 pnpm add A -r, 那么所有的 package 中都会被添加 A 这个依赖,当然也支持 --filter字段来对 package 进行过滤。

原理:

比如安装bar包,根目录只包含安装的包bar,而node_modules目录下的bar包会软链接到.pnpm/bar/node_modules/bar@…,被bar依赖的包会被提升到.pnpm的根目录,其他依赖该包的也会软链接到这里,而bar,foo包硬链接到.pnpm store;总之,软链接解决了磁盘占用的问题,而硬链接解决了包的同步更新和统一管理问题。

嵌套结构的问题在于:

  • 包文件的目录可能会非常长
  • 重复安装包
  • 相同包的实例不能共享

而扁平结构也同样存在问题:

  • 依赖结构的不确定性(不同包依赖某个包的不同版本 最终安装的版本具有不确定性)可通过lock文件确定安装版本
  • 扁平化算法复杂,耗时
  • 非法访问未声明的包

monorepo:

多个项目或者包文件放到一个git仓库管理,解决代码复用问题,开发流程统一,高效管理多项目/包