pnpm速成秘籍

137 阅读4分钟

pnpm已经🔥出天际,没有理由不试试pnpm了,使用任何工具都是要解决实际的问题,那么pnpm到底解决了什么问题,我们通过本文一起扫盲pnpm. 通过本文,我们将一起了解以下知识点:

  • 什么是pnpm
  • pnpm的特点
  • 为什么pnpm如此优秀
  • 日常使用指南

什么是pnpm

官方文档是这样说的

Fast, disk space efficient package manager

简单来说pnpm就是一个包管理工具,和npm/yarn是同一个范畴的,但是它不同与其他两个包管理工具的是:

  • 节省磁盘空间

pnpm的特点

  • 快速:pnpm 比其他包管理器快 2 倍
  • 高效:node_modules中的文件复制或链接自特定的内容寻址存储库
  • 支持mono:pnpm支持单仓多包
  • 严格:pnpm创建了非平铺的node_modules,因此代码无法访问任意包

为什么pnpm如此优秀

为什么pnpm能够脱颖而出呢,我们可以从两方面看下他做出的努力

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

  • 当我们依赖某个依赖项的不同版本时,不会将所有版本都安装到我们本地,而是只会把不同版本的差异部分添加到仓库中
  • 所有的文件都会存储到硬盘上的某一个位置,当我们安装某个软件包时,会将包里的文件链接到这个位置,如此一来,我们所有的项目都可以共享这些软件,不用重复安装这些包了

因此节约了我们磁盘上的大量空间,同事提高了安装速度,项目的依赖项越多,安装速度越快

创建非扁平化的node_modules

npm和yarn创建扁平化的node_modules,所以我们项目中可以访问到未被添加到当前项目的依赖项 pnpm则是使用软链的方式将项目的依赖项添加到模块文件夹的根目录

那么pnpm是如何管理我们的依赖包的呢,我们可以通过官网的一个图一探究竟:

image.png 简单解释下这个图:

  1. 项目依赖bar@1.0.0
  2. node_moddules目录下的bar@1.0.0,是个软链接,链向.pnpm/bar@1.0.0/node_modules/bar@1.0.0
  3. bar依赖foo@1.0.0,foo@1.0.0软链和.pnpm/bar@1.0.0/node_modules/bar@1.0.0处在同一个目录,但是软连接到.pnpm/foo@1.0.0
  4. .pnpm目录下的foo和bar硬链接到了store中的数据

因此,不论我们的依赖项的内部嵌套多么深,最后所有的包都会在.pnpm目录下以硬链接的方式连接到store中。我们包的依赖关系维护在该包的node_modules中,例如bar@1.0.0把它的依赖项foo和他自己bar@1.0.0包本身放在他自己的node_modules中,这样就很好的管理了包和他的依赖项,一眼看上去,就明晰了很多。而且我们项目的node_modules也干净多了,只有一个bar。

换言之,pnpm 摒弃了传统的复制模式,而是把全局 store 硬连接到项目的 node_modules/.pnpm,然后包之间通过软链接来维护依赖关系。

日常使用指南

如果你使用过npm和yarn,那你可以丝滑切换

  • pnpm init 初始化package.json
  • pnpm install <package-name> 安装包,pnpm install lodash -D
  • pnpm update 更新包
  • pnpm link 和npm使用方式一样

pnpm也支持mono,配置pnpm-workspace.yaml,配置packages字段即可,不在赘述。 同时pnpm也支持filter语法,指定范围更新package

玩转pnpm的Monorepo

  1. 安装pnpm
yarn global add pnpm
  1. 创建项目

创建pnpm项目

mkdir pnpm-playground
  1. 初始化项目
pnpm init
  1. 创建pnpm-workspace.yaml 这个文件定义了工作空间的根目录,内容如下:
packages:
  - 'packages/**'
  1. 在工作空间创建多个项目

创建vue-play文件夹和utils文件夹,并进入目录执行pnpm init 目录结构如下:

image.png

  1. 安装依赖 首先我们往utils中安装lodash依赖,这里我们不需要进入utils目录安装依赖,因为pnpm提供了过滤器--filter可以帮助我们往置顶的package中安装依赖,形如:
pnpm --filter <package_selector> <command>

其中--filter等价-F

pnpm -F utils add lodash-es

然后我们在utils中的index.js中导出如下内容:

import { random } from "lodash-es";

export default function () {
 return random()
}

然后我们在vue-play中添加utils作为依赖:

 pnpm -F vue-play add utils@*

其中@*表示使用最新的版本,这样的话有个好处就是可以省去每次都要同步最新版本的问题。

安装之后我们的package.json结构如下所示:

{
  "name": "vue-play",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "utils": "workspace:*"
  }
}

然后我们在vue-play的文件中的index.js中写入:

import getRandom from 'utils'

console.log(getRandom())

最后运行该文件node index.js,得到0或者1

总结

到此为止我们的pnpm的概念,特点,以及使用方法,包括Monorepo的实现,我们都有了一个初步的认识,未来有机会在实际场景中使用时,应该会遇到更多的知识点。我们不用着急吃撑大胖子,先有个印象就好了。

参考资料:

  1. pnpm 官方文档
  2. 关于现代包管理器的深度思考——为什么现在我更推荐 pnpm 而不是 npm/yarn?*