• 重学前端——npm yarn pnpm


    npm yarn pnpm

    npm

    NPM 是最初由 Node.js 项目开发的 JavaScript 包管理器。它使开发人员能够更轻松地在不同项目之间共享代码,并在自己的项目中使用其他人的代码。安装node.js会带npm

     npm get cache // 获取缓存地址
     npm -g root // 获取全局安装node_modules地址
     npm install -g npm // npm升级
    
    • 1
    • 2
    • 3

    npm install 过程

    1. 先检查配置 .npmrc 优先级最高

    2. 检查有没有package-lock.json文件

      • 有判断 跟package.json文件里的版本是否一致 一致就用package-lock.json的版本 不一致就重新安装依赖更新package-lock.json
      • 没有 根据package.json构建依赖树
    3. 检查有没有缓存 缓存存放在.npm目录 package-lock.json的integrity属性存放的是索引 根据这个索引去.npm/Content-v2 目录下找缓存

      • 如果有缓存 直接拿到这个缓存包 解压到项目的node_module目录下
      • 如果没有缓存 就去仓库下在缓存目录 再从缓存目录解压到node_module目录下
      • 更新package-lock.json

    npm包安装机制

    1. Npm1-npm2

      在安装依赖包时采用的是递归安装 ,根据dependencies 和 devDependencies来确定第一层依赖,根据第一层依赖的子模块。递归安装各个模块到子依赖的node modules中,直至依赖不再依赖其他模块。如果a b 同时依赖 c 那么 c会同时安装在a b 中, 导致大量冗余,node modules体积过大。

    2. Npm3为了解决问题 把依赖打平 ,扁平化结构,尽量把依赖都放到第一级 重复模块只放一个。当模块存在版本不兼容时,靠前的放在第一级,后面的放在依赖树中(上级依赖的node_modules)。虽然解决了node_modules体积过大的问题,但是带来了新的问题:package.json的依赖顺序放的不同 会导致依赖树也会不同,在代码运行时会出现问题。

    3. Npm5为了固定依赖树的顺序 出现了package-lock.json 它与node_modules中的包结构完全一致

      {
        "name": "cloudcontrol-fe",
        "version": "0.0.0",
        "lockfileVersion": 1,
        "requires": true,
        "dependencies": {
        	"@babel/generator": {
            "version": "7.18.10",
            "resolved": "https://registry-mirrors.avlyun.org/repository/npm-public/@babel/generator/-/generator-7.18.10.tgz",
            "integrity": "sha512-0+sW7e3HjQbiHbj1NeU/vN8ornohYlacAfZIaXhdoGweQqgcNy69COVciYYqEXJ/v+9OBA7Frxm4CVAuNqKeNA==",
            "dev": true,
            "requires": {
              "@babel/types": "^7.18.10",
              "@jridgewell/gen-mapping": "^0.3.2",
              "jsesc": "^2.5.1"
            },
            "dependencies": {
              "@jridgewell/gen-mapping": {
                "version": "0.3.2",
                "resolved": "https://registry-mirrors.avlyun.org/repository/npm-public/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
                "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
                "dev": true,
                "requires": {
                  "@jridgewell/set-array": "^1.0.1",
                  "@jridgewell/sourcemap-codec": "^1.4.10",
                  "@jridgewell/trace-mapping": "^0.3.9"
                }
              }
            }
          },
        }
       }
      
      • 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

      Version:版本

      resolved:下载地址

      Integrity: 包 hash 值 校验文件的一致性 完整性

      Requires: 对应子依赖

      Dependencies: 与外层结构一样 里面时子依赖 只有当这个子依赖的版本与最外层的依赖版本冲突时才有

    4. 幽灵依赖 没在package.json中声明 但是仍然能在代码中用到的 这会导致一些错误:依赖丢失,如果a依赖了b 在package.json中没有用到b 在代码中直接用到了b 但是假设a升级后不再依赖b 就会导致代码报错找不到b;不兼容版本 或者假设a升级了 b的版本变了 而代码有不兼容的api 也会导致代码报错。npm 和 yarn 都存在这个问题。

    yarn

    Yarn 是 JavaScript 的包管理器,由 Facebook 开发。它快速、可靠且安全。yarn是在npm2后出现的为了解决npm依赖安装过慢,嵌套黑洞洞问题,引入了扁平化安装 以及缓存机制 离线策略 yarn-lock

    yarn cache dir // 查看yarn的全局缓存目录
    
    • 1

    yarn install

    1. Checking 检测 检查是否有package-lock.json 有则提示不要混合使用
    2. Validating package.json 检测运行环境 package.json 中的os cpu engines
    3. Resolving packages 解析包
      • 收集首层依赖 dependencies devDependencies
      • 遍历所有依赖 从首层依赖开始 有yarn.lock的从lock中获取依赖的具体信息 没有的从registry中获取 讲包信息 加入依赖信息Map
      • 同上对子依赖案层递归处理
    4. Fetching packages 获取包
      • yarn会比较node_modules下的模块是否符合上面收集到的依赖信息 如果符合 就结束 打印 success Already up-to-date
      • Yarn 获取包时会先确定缓存中是否存在 不存在就去registry下载
      • 讲下载的压缩包写入缓存目录
      • 更新yarn.lock
    5. Linking Packages 连接依赖 将依赖复制到node_modules
      • 处理peerDependecies 当依赖不存在时 中断操作提示用户安装
      • 扁平化依赖树 首次出现的包放顶层 后面再出现的其他版本的包放在对应的子依赖树中
      • 拷贝依赖树到node_modules中
    6. Building Packages 执行install 阶段的脚本,编译依赖中的二进制包 构建安装
      • 执行 preinstall、 install、postinstall 这是因为有些包需要根据宿主机的环境动态生成模块,例如node-sass 通过node-gyp进行本地构建,通过node-gyp将binding.node格式的二进制文件构建成可以被nodejs执行的代码。由于需要node-gyp 和sass二进制文件 下载会慢,开始是通过修改sass-binary-site变量 指定到内网,node-gyp 需要配置disturl。 node-sass 已启用 用 sass

    pnpm

    pnpm是为了解决幽灵依赖的问题。PNPM 是一个新的 JavaScript 包管理器,它构建在 npm 之上,以简化节点应用程序中包的安装过程。PNPM 是 NPM 的替代品。它遵循与 NPM 相同的原则,但它具有一些附加功能,使其比其前身更强大。

    硬链接 软链接

    • 硬链接 是多个文件具有同一个索引结点号(index node) 有多个文件 所有文件同步更新 删除其中一个其他不会有影响 除非所以的都文件被删完 ln 文件1 文件2 创建文件1的硬链接文件2
    • 软链接 类似桌面快捷方式 软链接其实是一个文本 里面包含源文件的地址信息。例如1是2的软链接 1和2的index node 不同, 但是1中放着2的路径 可以根据1找到2 1和2是主从关系 删掉2 1是一个无效链接 ln -s 1.txt 2.txt

    安装机制

    • Store 目录 为了让所以包都只下载一次 不管什么版本 pnpm有个~/.pnpm-store/v3/files目录 存储所有的包 每次项目安装依赖时如果这里存在直接硬链接过去不必重复下载
    • project/Node_module/.pnpm目录 虚拟目录 nodejs 访问不到,存储当前项目的包的硬链接 所有包都摊平在这个目录(全是一级),对于子依赖包会放心父级依赖包下的node_modules里面,而这个node_modules里面的包其实是软链接 方便父级依赖包能找到子依赖包
    • project/Node_module 里面直接目录存的是package.json声明的包 其实是软链接到.pnpm里面的包

    假设一个项目依赖 a 和 b c包 a b又依赖d@1 c包依赖d@2,那么pnpm安装后的目录如下

    {
       node_modules {
           .pnpm {
              a@1 { 
                node_modules {
                  a: 硬链接 strore/a
                  d@1: 软链接到 ../../d@1/node_modules/d1
                }
              } 
              b@1  { 
                node_modules {
                  b: 硬链接 strore/b
                  d@1: 软链接到 ../../d@1/node_modules/d
                }
              } 
              c@1  { 
                node_modules {
                  c: 硬链接 strore/c
                  d@2: 软链接到 ../../d@2/node_modules/d
                }
              } 
              d@1  { 
                node_modules {
                  d: 硬链接 strore/d
                }
              } 
              d@2  { 
                node_modules {
                  d: 硬链接 strore/d
                }
              } 
           }
           a :软链接到 .pnpm/a@1/node_modules/a
           b :软链接到 .pnpm/a@1/node_modules/b
           c :软链接到 .pnpm/a@1/node_modules/c
       }
    }
    
    • 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

    package.json声明的包会在store存一份,项目的.pnpm虚拟目录会存对应的硬链接,node/modules上存.pnpm里面对应包的软链接,方便项目直接应用包;而对于子依赖包会全部摊平在.pnpm目录里面,而对应的依赖树关系会在父依赖包里面存对应的符号链接,方面父依赖包用到。

    优点

    • .pnpm是虚拟目录 nodejs访问不到,所有不会存在幽灵依赖,项目里面不能直接访问子依赖;
    • 所有依赖都摊平 所有不会导致不同版本的子依赖会下载多次的问题,节省磁盘空间;
    • 第二次安装依赖包时采用硬链接 安装速度快;
  • 相关阅读:
    Flask框架——flask-caching缓存
    [⑥5G NR]: 无线接口协议,信道映射学习
    Python之第十章 IO及对象列化
    10个WordPress的query_posts语句使用技巧
    Android开发学习日记--页面间传递数据
    EXCEL day 03 重要函数
    Java 并发编程在生产应用场景及实战
    万应案例精选|以“数”战“疫”,万应低代码敏捷开发筑牢抗“疫”堡垒
    python之列表介绍
    【Linux】第四站:Linux基本指令(三)
  • 原文地址:https://blog.csdn.net/zw52yany/article/details/126298357