重学前端——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升级

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"
              }
            }
          }
        },
      }
     }
    

    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的全局缓存目录

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
   }
}

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

优点

  • .pnpm是虚拟目录 nodejs访问不到,所有不会存在幽灵依赖,项目里面不能直接访问子依赖;
  • 所有依赖都摊平 所有不会导致不同版本的子依赖会下载多次的问题,节省磁盘空间;
  • 第二次安装依赖包时采用硬链接 安装速度快;

你可能感兴趣的:(npm,前端,javascript,yarn,pnpm)