从 NPM 迁移到 PNPM
PNPM 一直被誉为 NPM 的更高性能和更可靠的替代品,旨在减少缓慢的构建时间并消除依赖项不匹配的问题。我主要研究 PNPM 作为一种加快 CI 管道完成时间的方法,以及在开发周期中使用更好的包管理器。
我在一个仍处于 alpha 开发阶段的项目上测试了这一点,因此可以承受任何因错误或其他问题而导致的潜在停机。在部署应用程序时,用 PNPM 替换包管理器不太可能导致任何重大更改,但如果应用程序已经上线,则绝对应该首先在测试环境中进行测试。
唯一的潜在问题是依赖不匹配,但我已在本博文中更详细地描述了这一点。
Get started
PNPM 的安装指南可在此处找到。对于 JS 开发人员来说,最简单的方法可能是运行:npm install -g pnpm。
在要转换为 PNPM 的项目中,找到 node_modules 目录并将其删除。
将以下代码添加到项目的 package.json。这将阻止人们使用任何其他包管理器安装包。
1
"scripts": { "preinstall": "npx only-allow pnpm", ... }
在目录根目录中,创建一个名为的文件pnpm-workspace.yaml并添加以下内容 (Optinal)
如果项目要使用pnpm workspace,则需要添加如下配置1
2
3
4
5
6packages:
# include packages in subfolders (change as required)
- 'apps/**'
- 'packages/**'
# if required, exclude directories
- '!**/test/**'在终端中运行pnpm import。这将根据当前的 yarn.lock 或 package-lock.json 创建一个 pnpm-lock.yaml 文件。
删除 yarn.lock 或 package-lock.json 文件。
通过运行pnpm i或使用 pnpm install 安装PNPM依赖项
如果你的 package.json 中有使用前缀的脚本npm run,则需要将其替换为pnpm 例如:
pnpm test
,而不是npm run test
重要的提示
使用 NPM 或 Yarn 安装依赖项时,会创建一个“flat”node modules 目录。这意味着源代码可以访问未作为依赖项添加到项目的依赖项。PNPM 的工作方式不同,它使用符号链接仅将项目的直接依赖项添加到模块目录的根目录中。
例如,如果你有A一个导入包的包B
import something from ‘B’
但没有在项目的依赖项中明确指定依赖B,那么执行将失败。
这种新结构不仅提高了构建性能,还降低了项目中出现依赖性错误的可能性。
如果你确实遇到了需要扁平节点模块结构(比如 NPM 或 Yarn 创建的那种结构)的情况,PNPM 提供了一个解决方案:
1 | pnpm install --shamefully-hoist |
尽管应尽可能避免这种情况,因为它违背了 PNPM 实现的设计模式。您可能需要使用此功能的一个示例是,您安装的依赖项未在package.json中明确指定。
使用pnpm进行构建
第一次运行时,pnpm install您将在终端中看到一个进度图,如下图所示:
请注意,首次安装时“已重用”计数保持为 0。这是因为我们尚未创建 PNPM 可以引用的缓存。
一旦安装了所有依赖项,如果您pnpm install再次运行或添加新包pnpm add some-new-package -w,您将看到“重复使用”计数器现在正在上升。
这种缓存可显著加快安装过程,因为它可避免重新下载已获取的软件包。软件包也是同时下载,而不是逐个下载。
“在 pnpm 中,如果包已经安装在其他项目中,则总是会重复使用,从而节省大量磁盘空间,这使得它比 npm 更快、更高效。”
PNPM 和 CI 管道
我第一次研究使用 PNPM 作为替代包管理器的主要目的是加快我的 CI 管道的时间,即使对于不是特别复杂且没有端到端测试要运行的项目,该管道也经常需要 10 或 15 分钟。
考虑到这一点,这里有一个示例.gitlab-ci.yml文件,其中包含一个简单的部署脚本到 Gitlab page,展示了我如何使用 PNPM。
1 |
|
您可以看到,在此before_script阶段,我们发出 CURL 请求来下载 PNPM。然后,我们将存储路径设置为新 PNPM 缓存存储的位置。
pnpm store path此路径可能因您的项目而异。我通过添加到我之前的 CI 脚本、运行管道,然后复制/粘贴它给我的路径,找到了正确的路径。
在此之后,我们确保注册表正在使用 Nexus 来安装包。
下一个重要部分是“缓存”部分。这很可能与您现有的设置类似,只是我们需要添加到“路径”:
前面提到的 PNPM 存储路径,例如/root/.pnpm-store/v3
./node_modules
通常我们会.npm在路径数组中使用它,但现在可以将其删除。
我们利用缓存键$CI_COMMIT_REF_SLUG,它允许我们在同一分支(例如 master)中的作业之间共享缓存。初始运行后,当我们运行管道时,如果 PNPM 成功命中缓存,我们应该会看到“reused”计数器上升。
最后,在本script节中,我们运行新pnpm install命令,然后运行项目相关的命令,例如 build-storybook。
其他 PNPM 功能
PNPM 的另一个有用功能是能够管理 Node 版本。我们很多人目前都使用nvm它,它的工作方式几乎相同。
一些例子:
- 安装 Node 的 LTS 版本:pnpm env use –global lts
- 安装node 16:pnpm env use –global 16
- 安装最新版本的 Node:pnpm env use –global latest
- 删除特定版本的 Node:pnpm env remove –global 14.0.0
与我们当前使用的流程相匹配的一个有用示例是.npmrc在项目根目录中有一个定义 Node 版本的文件:
1 | use-node-version=16.14.0 |
当我们运行 时pnpm start,它将从配置文件中获取此 Node 版本并将其用于我们的项目。能够使用我们的默认包管理器管理 Node 版本非常方便。
最后说明
您可能需要将其添加.pnpm-store/**到您的.gitignore文件中。
PNPM的文档非常有用且非常详细。
从 NPM 迁移到 PNPM