• pnpm和yarn2 pnp的对比


    工作中我们会遇到同时支撑多个vue项目,而且是频繁修改多个项目,我们本地会有clone多个项目并且每个项目有自己的node_modules,导致我们的电脑磁盘爆满,偶尔删一下空出来磁盘空间,过两天又要修改刚删过的文件不得不安装过来;苦–!

    找问题的话 就发现我们每个项目去掉node_modules文件以及.git 或者一些其他的依赖后 项目本身源文件大小也就20M,但是加上这些依赖后我们的项目会有500M-600M

    再接着找问题的话发现和使用npm或者yarn时的安装的依赖有很大关系,如果你有100个项目使用了某个依赖,就会有100份依赖的副本保存在磁盘上,即使我们删除了部分项目的依赖,只要我们重新安装,我们清除的磁盘就又爆满了;(对于pnpm,依赖项将存储在一个内容可寻址的仓库中上,实现了节约磁盘空间并提升安装速度)

    除此之外, Node 的模块机制还有以下缺点:

    • Node 本身并没有模块的概念, 它在运行时进行查找和加载。你可能在开发环境运行得好好的, 可能到了线上就运行不了了, 原因是一个模块没有添加到 package.json

    • Node 模块的查找策略非常浪费.(递归遍历所有的项目依赖关系) 这个缺点在大部分前端项目中可以进行优化,比如 webpack 就可以限定只在项目根目录下的 node_modules 中查找, 但是对于嵌套的依赖, 依然需要 2 次以上的查找

    • node_modules 不能有效地处理重复的包. 两个名称相同但是不同版本的包是不能在一个目录下共存的.

    # ① 假设项目依赖a,b,c三个模块, 依赖树为:
    ├─── a
    │    ├── vue@3
    ├─── b
    │    ├──vue@2
    ├─── c
         ├──vue@2
    # yarn安装时会按照项目被依赖的次数作为权重, 将依赖提升(hoisting),
    # 安装后的node_modules结构为:
      .
      └── node_modules
          ├── a
          │   ├── index.js
          │   ├── node_modules
          │   │   └── vue  # @3
          │   └── package.json
          ├── b
          │   ├── index.js
          │   └── package.json
          ├── c
          │   ├── index.js
          │   └── package.json
          └── vue  # @2 被依赖了两次, 所以进行提升
    
    # ② 现在假设在①的基础上, 根项目依赖了react@15, 对于项目自己的依赖肯定是要放在node_modules根目录的,
    # 由于一个目录下不能存在同名目录, 所以react@16没有的提升机会. 
    # 安装后node_moduels结构为
      └── node_modules
          ├── a
          │   ├── index.js
          │   └── package.json # vue@3 提升
          ├── b
          │   ├── index.js
          │   ├── node_modules
          │   │   └── vue  # @2
          │   └── package.json
          ├── c
          │   ├── index.js
          │   ├── node_modules
          │   │   └── vue  # @2
          │   └── package.json
          └── vue  # @3
    # 上面的结果可以看出, vue@2出现了重复
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    那么,有没有什么好的方式解决吗?
    这里会主要推出以下两位包管理器:

    • yarn2 pnp
    • pnpm快速的,节省磁盘空间的包管理工具

    pnpm

    Pnpm 是node.js的包管理工具,然而并不是npm或者yarn的优化兼容模式,通过对node_modules的处理,让模块的管理更加快速和高效。

    • Pnpm 运行起来非常的快,超过了npm和yarn是同类工具速度的将近 2 倍
    • node_modules 中的所有文件均链接自单一存储位置
    • Pnpm 内置了对单个源码仓库中包含多个软件包的支持
    • Pnpm 创建的 node_modules 并非扁平结构,因此代码不能对任意软件包进行访问
    • pnpm pnpm采用了一种巧妙的方法,利用硬链接和符号链接来避免复制所有本地缓存源文件,这是yarn的最大的性能弱点之一,内部使用基于内容寻址的文件系统来存储磁盘上所有的文件

    下面会讲一下pnpm解决npm和yarn的现有存在问题,以及是如何节省磁盘空间和提升安装速度的。

    vue 项目使用pnpm

    Vue cli已经默认添加了对pnpm的支持,所以项目切换pnpm的方式可以直接切换,支持vue2 vue3:

     npm i -g pnpm
     rm -rf node_modules
     rm -rf yarn.lock
     
     # 安装依赖 第一次安装会耗费一点时间
     pnpm install
     
     # 运行服务
     pnpm run serve
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    可以看到pnpm方式安装的依赖,可以看到如下变化:

    • 保留了node_modules文件,避免项目破坏性升级,向下兼容友好,
    • 根目录下的node_modules下面不再是眼花缭乱的依赖,而是跟 package.json 声明的依赖基本保持一致
    • 添加了pnpm-lock.yaml文件存放着所有依赖关系图
    • node_modules下主要为项目声明主依赖,在node_modules/.pnpm文件夹下则扁平放着所有项目所有依赖软连,这样的好处可能对于pnpm来说查找更容易
    • 将包本身和依赖放在同一个node_module下面,与原生 Node 完全兼容,又能将 package 与相关的依赖很好地组织到一起,设计十分精妙

    常用命令

    pnpm install xxx # 安装包
    pnpm update xxx # 更新包
    pnpm uninstall xxx # 卸载包
    
    • 1
    • 2
    • 3

    查看更多

    yarn2 pnp

    Yarn 2 最大的特点是它的 Plug’n’Play 系统(以下简称 PnP)。虽然 Yarn 1 也可以使用 PnP,但是 Yarn 2 默认开启了它。
    Pnp的产生也是因为node_modules,它是社区尝试替代node_modules的一个方案。在第一次安装依赖后,除了yarn.lock,Yarn 2 还会创建一个.pnp.js文件。通过这个文件,Yarn 可以主动地告诉 Node.js 应当在哪个具体的位置找到某一个包,而不是让 Node.js 自己尝试在 node_modules 目录中寻找依赖。通过这种方式,Yarn 可以把所有的包以 zip 的格式储存起来,甚至让不同的工程共用同一份依赖文件,从而减少依赖的体积和加载时间。

    然而,目前的 Node.js 社区工具几乎都是基于 node_modules 进行开发的。这导致 Yarn PnP 和目前的很多工具都不兼容,即使webpack、rollup等的支持,仍然没有让yarn pnp普及起来。

    安装使用

    初始化之前,项目下吧。yarnrc.yml .yarnrc删除

    1. 切换到1x版本
      yarn set version latest
    > output
    Downloading https://github.com/yarnpkg/yarn/releases/download/v1.22.10/yarn-1.22.10.js...
    Saving it into /Users/xiaoshao/Downloads/ROOT/pnpm/vue3-test-pnp/.yarn/releases/yarn-1.22.10.cjs...
    Updating /Users/xiaoshao/Downloads/ROOT/pnpm/vue3-test-pnp/.yarnrc...
    Done!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 切换到2x版本
      yarn set version berry
    > output
    Resolving berry to a url...
    Downloading https://github.com/yarnpkg/berry/raw/master/packages/berry-cli/bin/berry.js...
    Saving it into /Users/xiaoshao/Downloads/ROOT/pnpm/vue3-test-pnp/.yarn/releases/yarn-berry.cjs...
    Updating /Users/xiaoshao/Downloads/ROOT/pnpm/vue3-test-pnp/.yarnrc.yml...
    Done!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在yarnrc.yml 添加如下配置:

    npmAlwaysAuth: true
    
    npmAuthToken: 7BPLCVtdNJLsH7vJT5Cm9g==
    
    npmRegistryServer: "http://192.168.x.x:9999/"
    
    unsafeHttpWhitelist:
      - 192.168.1.1
    yarnPath: .yarn/releases/yarn-berry.cjs
    enableGlobalCache: true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    yarn install 开始安装依赖

    查看配置

    yarn config
    
    • 1

    查看目录结构

    .yarn 目录结构
    ├── releases
    │   └── yarn-berry.js
    │—————— .yarnrc.yml
    
    • 1
    • 2
    • 3
    • 4

    工程根目录下会新增一个.yarn目录及一个.yarnrc.yml的配置文件:

    • .yarn目录:存放Yarn 2的具体执行文件、由Yarn 2安装的依赖等
    • .yarnrc.yml配置文件:此工程针对Yarn 2的具体配置文件,和.npmrc、.yarnrc功能类似,这里要注意Yarn 2不会再读取.npmrc、.yarnrc中的配置,同时文件扩展也必须是yml,.yarnrc不能生效

    .yarn和.yarnrc.yml都应该提交到仓库

    vue项目依赖报错解决

    默认情况下完成上述操作后,webpack构建相关项目无法正常运行,需按照下面配置

    yarn install pnp-webpack-plugin -D
    
    • 1
    // vue.config.js
    const PnpWebpackPlugin = require('pnp-webpack-plugin')
    module.exports = {
      configureWebpack: {
        resolve: {
          plugins: [PnpWebpackPlugin],
        },
        resolveLoader: {
          plugins: [PnpWebpackPlugin.moduleLoader(module)]
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    yarn2 pnp 和node_modules切换

    # .yarnrc.yml
    yarnPath: ".yarn/releases/yarn-berry.js"
    
    # 如果工程跑不起来可以先尝试增加下面的配置:
    nodeLinker: "pnp"
    pnpMode: "loose"
    
    # 如果仍然跑不起来,可以用下面的配置完全按照以前的依赖按照方式
    nodeLinker: "node-modules"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    需要讨论项

    因为Yarn 2通过将所有npm包以zip包的形式存在store中,以zip包的方式管理依赖,大大减少了文件数量和文件体积,故而使得工程的依赖能够进行版本管理,进而能够完美地实现依赖锁定。

    基于上述场景,部分人提出将工程依赖都提交到仓库中了,协作者拉取代码后甚至都不再需要执行依赖安装的步骤。

    上面的论题,只能说有一定的好处,但是个人观点更觉得弊大于利。

    杂项

    Yarn 1 的一个很大的问题是yarn.lock中会出现依赖版本的重复和落后的问题。这个问题曾经造成了一些令人非常疑惑的现象。Yarn 2 原生支持了yarn deque命令,可以说从根本上解决了这个问题。

    总结

    同一份代码分别在yarn/yarn2 pnp/pnpm下安装依赖后的大小查看,结果应该很明显

    • yarn2 pnp 不降反升了体积
    • pnpm体积确实缩小了一点点,就跟你120斤瘦了2斤的感觉一样,就是稍微轻了一点点

    整体使用感受下来,

    • pnpm 优点:确实更容易简单一些,报错少,入门低,且对项目兼容性也够好,确实也是有一定的效果;缺点:在安装层面没有优化,每次都是需要重新下载安装依赖,所以安装速度变化没有太大
    • yarn2 pnp 优点:通过全局store能在安装速度上得到提升,毕竟使用了本地缓存;缺点:在文件目录结构node_modules上暂时没有看到很大的优化,体积在一定程度上没有降低,可能还会增加体积

    总结来说:确实能感受到Yarn pnp在处理的精妙之处,目前仍是觉得稍微不稳定,但是感觉仍需要几个小版本升级可能才会真的普及起来;而pnpm已经发展了很多年,也确实在实用性、稳定性上有一定的作用。

    参考资料

  • 相关阅读:
    Git工具使用全解
    式子表达ds类——多用位置/值域表示未知数+区间覆盖转区间加:CF407E
    TCP的三次握手和四次挥手
    免杀对抗-内存加载-shellcode转换-UUID+MAC+IPV4
    vue、全局前置守卫
    msvcr120.dll放在哪里?怎么修复msvcr120.dll文件
    正则匹配语法学习
    java框架-Springboot3-场景整合
    DataGrip 连接 Centos MySql失败
    Matlab/Simulink Coder: 代码生成中的数据处理类型控制
  • 原文地址:https://blog.csdn.net/kang_k/article/details/126872314