优化实战 第 61 期 - 使用 pnpm 提升工程化效率

2,232 阅读4分钟

包管理工具 npmyarn 都是很优秀的,但也存在一些潜在的问题。当相互依赖包的数量达到一定的量级时,不仅会占用大量的磁盘空间,还会大大影响包的拉取速度。

所以需要更优秀的 pnpm 包管理工具来进行优化,从而打造一个高效的工程化环境

工具 npm2 的工作原理

  • 假设场景

    有三个模块:A、B 和 C。A 依赖于 B 的 v1.0 版本,C 依赖于 B 的 v2.0 版本

    example.png

  • 终端结果

    tree.jpeg

    可以通过 npm ls 查看所有依赖项关系

    npmls.jpeg

    也可以通过 npm ls --depth=0 查看主要依赖项

    npmlsdepth0.jpeg

  • 工作原理

    以嵌套的方式安装所有依赖项

  • 痛点问题

    当包中创建了太深的依赖树时,在 Windows 上的目录路径过长,就会导致无法删除

    当一个包被多个依赖项依赖时,它会被多次复制粘贴并生成多份文件,就会导致磁盘空间浪费

  • 延伸知识

    Windows 下文件路径有 260字符长度限制,可以通过调用 Unicode 版的 Windows API 函数来绕过限制

工具 npm3+ 的工作原理

  • 假设场景

    有两个模块:A 和 B。A 依赖于 B

    npm3deps2.png

    有三个模块:A、B 和 C。A 依赖于 B 的 v1.0 版本,C 依赖于 B 的 v2.0 版本

    npm3deps4.png

  • 终端结果

    npm3tree.jpeg

    可以通过 npm ls 查看所有依赖项关系

    npm3npmls.jpeg

    也可以通过 npm ls --depth=0 查看主要依赖项

    npm3npmlsdepth0.jpeg

  • 工作原理

    以依赖扁平化的方式安装所有依赖项,减轻嵌套导致的深层树和冗余

    但是,顶级依赖是唯一的,且只能有一个版本,所以不能将顶级依赖的其它版本也安装为顶级依赖项。这种情况下,npm v3 将默认为 npm v2 行为并将新的、不同的模块版本依赖项嵌套在需要它的模块下来进行处理

  • 与 npm2 的主要区别

    目录结构中的位置不再预测依赖项的类型(主要、次要等)

    依赖关系的解决取决于安装顺序,或者安装顺序将改变 node_modules 目录树结构

  • 痛点问题

    幻影依赖:模块可以访问 package.json 中未注册的包,开发者也可以直接引入使用,影响安全性

    扁平化算法本身的复杂性很高,对于多层次嵌套的文件耗时明显增加

工具 pnpm 的工作原理

  • 软链接和硬链接

    软链接(symbolic link) 可理解为指向源文件的指针,它是单独的一个文件,仅仅只有几个字节,它拥有独立的 inode

    硬链接(hard link) 与源文件同时指向一个物理地址,它与源文件共享储存数据,它俩拥有相同的 inode

  • 节约磁盘空间并提升安装速度

    使用 pnpm 时,依赖会被存储在内容可寻址的存储中。

    文件内容发生变化后将会按需更新,而不是全量复制整个新版本包的内容;所有文件都存储在硬盘上的某一位置,当包被安装时,包里的文件会硬链接到这一位置,而不会占用额外的磁盘空间

    允许跨项目地共享同一版本的依赖,速度比其它包管理工具快两到三倍

    pnpm.png

  • 创建非扁平化的 node_modules 文件夹

    node-modules-structure.jpg

    使用软链接的方式将项目的直接依赖添加进模块文件夹的根目录

  • 常用命令

    npm install pnpm -g pnpm安装
    
    pnpm add <pkg>	安装软件包到 dependencies
    
    pnpm add -D <pkg>	安装软件包到 devDependencies
    
    pnpm add -g <pkg>	全局安装软件包
    
    pnpm install 或 pnpm i	下载项目所有依赖项
    
    pnpm update 或 pnpm up	遵循 package.json 指定的范围更新所有的依赖项
    
    pnpm update -g <pkg>	从全局更新一个依赖包
    
    pnpm remove <pkg>	从项目的 package.json 中删除相关依赖项
    
    pnpm remove -D <pkg>	仅删除开发环境 devDependencies 中的依赖项
    
    pnpm remove <pkg> -g	从全局删除一个依赖包
    
    pnpm run <script> 或 pnpm <script>  运行脚本
    

工具 pnpm 性能总结

  • 快速

    pnpm 是同类工具速度的将近 2

  • 高效

    node_modules 中的所有文件均克隆或硬链接自单一存储位置

  • 支持单体仓库

    pnpm 内置了对单个源码仓库中包含多个软件包的支持

  • 权限严格

    pnpm 创建的 node_modules 默认并非扁平结构,因此代码无法对任意软件包进行访问