pnpm:是时候对npm说再见了
🌟pnpm
的背景和由来 🌟
PNPM
是一种包管理工具,始于 2016 年。该工具与其他包管理工具相比具有多种独特的特征,例如支持符号链接、快速安装速度,以及支持多语言开发。那么,pnpm
的由来是什么呢?
在之前的一些包管理工具(如npm
或者yarn
)内,为了防止包的版本过多,一般都是将包下载到项目的 node_modules 目录中。但是,这种方法可引发一些问题,例如每个包都要占用磁盘空间等。因此,常常需要定期将旧版本的包删除。
然而,这仅仅是缓解问题的方法,问题的根源在于大量依赖包的冗余复制,这会导致整个项目包的大小快速增加。而 PNPM
已经成功地解决了这个问题。
具体而言,pnpm
主张的是将每个包仅在磁盘上保存一份,既在项目根目录下使用一个 node_modules
文件夹,每个包则使用符号链接的方式链接到这个目录下。这样做,可以显著减少磁盘空间占用并缩短安装时间。
因为其设计理念和优势,pnpm
受到越来越多的关注,成为webpack
等许多重要工具的默认包管理器。
🧐 分析pnpm
与其他包管理工具的区别与优势 🧐
包管理工具的区别
在选择符合自己工具的包管理工具时,我们应该认真比较它们之间的区别。作为主流的三种包管理工具,pnpm
、npm
和yarn
使用了不同的包下载和管理方式。
-
npm
: 最初发布于 2010 年,是一种由Node.js
提供的默认包管理器。npm
存储依赖的包版本在node_modules
文件夹
中,这意味着相同的模块可能会在不同项目中多次存在,这会增加磁盘空间的占用,并且在安装依赖的时候会比较慢。 -
yarn
: 2016 年发布的 yarn,是由Facebook
、Google 和 Tilde 联合开发的一个包管理器。与npm
不同,yarn 使用lockfile
机制,确定了每个包的具体版本。yarn 使用yarn.lock
文件与无法很好地同时支持npm
和yarn
以及node_modules
目录占用过多的问题。 -
pnpm
: 2016 年发布的pnpm
,则使用了一些更独特的方式来提高包下载和管理效率。pnpm
在安装和更新依赖的时候,采用 symbolic links 来代替npm
和yarn
下载的多个实例,其中硬链接和符号链接的混合组成,从而降低了依赖占用的磁盘空间
pnpm
的优势
除了与npm
和yarn
相比的区别之外,pnpm
的一些独特设计也给它带来了显著的优势。
-
快速安装: 由于
pnpm
使用了链接的方式来管理包,因此无需重新复制每个包文件,使得一次安装所需的时间大大减少。 -
节省磁盘空间: 由于同一个包只被保存在磁盘上一次,因此
pnpm
适用于多个项目使用同一依赖的情况。通过使用本地缓存,多个项目可以共享依赖包,极大地减少了磁盘空间的使用。 -
支持多语言开发:
pnpm
不仅支持Node.js
,还包括许多其他的编程语言,例如TypeScript
、Reason 等。这使得使用pnpm
的我们可以更容易地在一个项目中使用多种语言进行开发。 -
易于使用:
pnpm
基本命令与npm
相同,所以只需要在使用上进行简单的修改即可。PNPM
具有完善的文档和有效的社区支持。
以上就是对pnpm
和其他包管理工具的区别与优劣势的分析,我们看到,pnpm
的特殊设计使之成为了一个高效的包管理工具,值得我们们尝试。
🔗 硬链接和符号链接 🔗
在介绍pnpm
中硬链接和符号链接的应用之前,我们需要先了解硬链接和符号链接这两种链接方式。
硬链接:是Linux
中一种文件系统链接的方式。当执行ln -P
创建一个硬链接时,硬链接和原文件共享inode
和block
,该硬链接可以视作源文件的另外一个别名。硬链接共享文件的访问和更改权限,因为硬链接和源文件的数据指针和inode
是相同的。也就是说,不论你打开源文件还是硬链接,看到的都是同一份数据。
符号链接:也叫软链接,是在文件系统中创建的一种特殊文件,它可以连接到其他文件或目录。在符号链接创建时,它会新建一个inode
,该inode
记录了被符号链接所指向的目标inode
。可以理解为一个指针,当你打开符号链接时,会从符号链接所指向的目标inode
处读入数据并处理。
相对而言,硬链接的速度比符号链接更快,因为硬链接只创建一个文件副本,多个文件链接指向同一个文件,而符号链接则是创建一个独立的文件,并指向原文件。
📂pnpm
中的硬链接和符号链接 📂
当我们使用pnpm
安装依赖时,它会在全局缓存目录中创建硬链接和符号链接。目录下的每个包将被存储在对应的缓存目录中,执行pnpm install
的时候,pnpm
会在每个项目中生成一个 node_modules 文件夹,然后使用符号链接将不同的依赖项放在这个文件夹内,完成依赖管理。
下面是一个pnpm
硬链接和符号链接的例子:
node_modules
├── acorn -> ../.pnpm/acorn@8.6.0/node_modules/acorn
├── axios -> ../.pnpm/axios@0.21.0/node_modules/axios
├── commander -> ../.pnpm/commander@8.0.0/node_modules/commander
├── express -> ../.pnpm/express@4.17.1/node_modules/express
...
这里的 ../.pnpm/
是全局依赖缓存的地址,每个依赖项都是用符号链接创建的。当pnpm
在实际的项目中使用依赖项时,它会将符号链接重定向回全局安装的包,从而减少磁盘的使用。由于硬链接共享一个inode
,因此可以使得执行命令更加快速。
通过结合硬链接和符号索引,pnpm
有效地将文件目录的大小限制在最小范围内,同时确保了快速本地安装速度。
💡pnpm
的使用方法和基本命令 💡
安装
我们可以使用npm
来安装pnpm
。在命令行中运行以下命令即可:
npm install -g pnpm
这将全局安装pnpm
。
配置
默认情况下,pnpm
的数据存储在~/.pnpm-store
文件夹中,我们可以通过环境变量PNPM_STORE_DIR
来更改存储位置,例如:
PNPM_STORE_DIR=/data/pnpm pnpm install
这将pnpm
的缓存存储在/data/pnpm
文件夹中。
使用
相比npm
,pnpm
的基本命令几乎相同,可以使用常规的install
、update
、list
、uninstall
等命令。以下介绍几个常用命令:
pnpm install
: 安装依赖包。pnpm update
: 更新依赖包到最新版本。pnpm list
: 查看当前项目的依赖关系。pnpm uninstall <package-name>
:卸载指定的依赖包。pnpm run <script>
:执行已经定义的不同的脚本命令。
值得注意的是,pnpm
默认会将执行命令的项目目录中的node_modules
文件夹替换为符号链接。这意味着符号链接上的内容是实时更新的,这对多个项目共享资源的情况特别有用。
常见问题和解决方案
1. pnpm
安装的速度比npm
或yarn
更快,为什么?
pnpm
通过使用链接的方式来管理包,在安装和更新依赖的时候,它使用符号链接代替复制文件,因此无需反复复制文件或下载文件,可以使安装速度大大提高。
2. 如何清除pnpm
的缓存?
可以在命令行中使用以下命令来清除pnpm
的缓存:
pnpm cache clean
或者,可以在指定的目录下使用以下命令:
pnpm cache clean --store ./cache-folder
3. 如何强制重新加载pnpm
缓存?
可以使用以下命令强制重新加载pnpm
缓存:
pnpm install --force
4. 如何升级pnpm
?
可以使用以下命令升级pnpm
:
npm install -g pnpm@latest
非常好,下面是我们所需的第四部分内容。
🚀pnpm
的最佳实践 🚀
使用pnpm
的workspaces
功能
使用pnpm
的workspaces
功能可以将多个项目捆绑在一起,这样可以更方便地管理这些项目的依赖。workspaces
将多个项目组成一个整体,因此我们可以更轻松地管理所有项目的依赖。我们可以使用类似以下的方法来配置workspaces
:
{
"name": "root",
"private": true,
"workspaces": ["packages/*/package.json"]
}
这意味着我们在packages
目录下的所有子目录中都可以使用workspaces
功能。
使用pnpm workspace
和shrinkwrap
pnpm install
命令在workspace
中的执行方式与单独运行在某个包的**npm install
** 或 yarn install
命令有所不同。例如,运行pnpm install
命令将自动安装所有子项目的依赖项。
有时,我们可能需要确保每个项目使用相同的依赖项版本,以此来保证项目之间的可靠性。在这种情况下,使用shrinkwrap
文件非常有用,它可以确保每个项目使用相同的依赖项版本。我们可以使用以下命令来创建shrinkwrap
文件:
pnpm install --shrinkwrap-only
使用pnpm
的.workspace
文件
当我们需要特定的命令在所有的workspace
中执行时,可以使用.workspace
文件。例如,所有workspace
都需要使用相同的eslint
配置,我们可以在root
目录下的.workspace
文件中放置以下内容:
{
"env": {
"NODE_ENV": "production"
},
"scripts": {
"lint": "eslint src/**"
}
}
这样做的好处是,这个配置将可用于整个workspace
,而不仅仅是一个单独的项目。
通过pnpm script
执行依赖
pnpm
可以在全局缓存中执行脚本。使用 pnpm run
命令时除了运行在根项目中定义的脚本, 它也会查找全局缓存中的脚本。我们可以使用以下命令来执行容器中的任何可执行文件:
pnpm run my-script
其中,my-script
必须是在容器中安装的可执行文件。这种做法在进行复杂的构建过程时非常有用,这样就不需要重复安装相同的依赖项。
使用pnpm
的多语言支持
pnpm
并不局限于Node.js
,它同样适用于其他语言。在使用不同的编程语言共同开发一个项目时,可以使用pnpm
作为包管理器,这样可以更方便地管理各个项目的依赖。
在管理多个不同语言的项目时,我们可以设置PNPM_LANG
环境变量来指定每个项目的默认语言,例如:
PNPM_LANG=typescript pnpm install
此外,pnpm
还支持安装不同语言的依赖项。例如,我们甚至可以在JavaScript
项目中安装C/C++
依赖项。
🏢 workspace
的用法详解
在使用pnpm
时,我们可以使用workspace
功能来管理多个项目的依赖关系。在这一部分中,我们将详细介绍workspace
的用法,包括如何配置、安装依赖、更新依赖、使用别名等等。
配置workspaces
要启用workspace
功能,我们需要在根目录下创建一个package.json
文件,并使用workspaces
属性来指定哪些文件夹被视为workspace
。例如:
{
"name": "my-project",
"version": "1.0.0",
"private": true,
"workspaces": ["packages/*"]
}
上面的配置将使my-project
项目中的packages
文件夹下的所有子目录成为workspace
。
安装依赖
在安装workspace
中的依赖时,我们可以使用pnpm install
命令,pnpm
会自动为每个workspace
安装它们的依赖项。例如:
pnpm install
这将为所有workspace
安装依赖。我们也可以使用pnpm install
命令为特定的workspace
安装依赖。例如:
pnpm install --workspace my-package
这将为名为my-package
的workspace
安装依赖。
更新依赖
在更新workspace
的依赖时,我们可以使用pnpm update
命令。这将为所有workspace
中的依赖更新并安装最新版本。如果我们希望只更新特定的workspace
依赖,可以使用--workspace
标志:
pnpm update --workspace my-package
这将只更新名为my-package
的workspace
依赖。
别名使用
当我们在workspace
中使用某个依赖项,并希望像在普通的Node.js
项目中一样使用它时,就需要使用别名功能。别名可以让我们将workspace
中的一个模块映射到另一个模块。为此,需要在package.json
文件中指定别名。
例如,我们可以在package.json
文件中添加以下内容:
{
"name": "my-project",
"version": "1.0.0",
"private": true,
"workspaces": ["packages/*"],
"dependencies": {
"my-utils": "workspace:*"
}
}
这将在 workspace 中安装my-utils
包,并将其指定为别名"workspace:*"
,这意味着可以像在普通Node.js
项目中一样引用它。