关于项目依赖

674 阅读18分钟

本篇文章是关于项目依赖的一些疑问整理,主要是平时工作中积累的一些问题。比如node_modules的作用、不同包管理器的特性等等。

node_modules

node_modoules是我们npm库存放的地方,它是我们使用三方能力的基础。

比如我们现在需要一个url转图片二维码的能力,要么我们自己去了解二维码生成规则,然后用canvas画一个出来;要么去寻找社区解决方案,使用三方能力。显然实际项目开发过程中,我们都会选择后者。理由很明显,这是一个常见的需求,社区肯定有成熟的解决方案,并且经过社区实践,它足够健壮。另一方面,我们只是简单使用,完全没必要花学习成本在这上面,得不偿失。

经过一番查找,我们选定了qrcode这个包

// 第一步 安装npm包
npm install --save qrcode

// 第二步 引入
import QRCode from 'qrcode'

// 使用
QRCode.toDataURL('https://www.baidu.com')
  .then(url => {
    console.log(url)
  })
  .catch(err => {
    console.error(err)
  })

简单两步,我们就获得了生成二维码的能力(感谢开源库作者)。接下来我们稍微深入的了解下这套系统的运作。

如何找到一个满意的npm包

首先来认识npm包发布网站 www.npmjs.com, 利用它的搜索功能,可以检索发布到npm上面的所有包。

比如我们直接搜索qrcode关键字

image.png

一般我们按默认排序就可以了,我们点第一个链接进去。(注:质量quality仅代表npm网站的计算规则所得出来的值,比如版本数量,更新频率,未关闭的issue等,不代表当前包的代码质量。)

image.png

基本上根据上面的指标和功能,我们就能确定这个包是否符合我们的要求。

除了上面npm网站以外,我们还可以去开发者社区寻找答案,一般都能找到当前流行的库。之后最好再去网站上看下目标库的指标,根据实际情况选用。

通过package.json认识三方库

熟悉一个三方库,我们一般从它的package.json文件入手,下面列出了需要重点关注的属性。

{
  "name": "acorn", // 项目名(重要)
  "description": "ECMAScript parser", // 描述信息
  "homepage": "https://github.com/acornjs/acorn", // 项目主页
  "main": "dist/acorn.js", // 入口文件
  "types": "dist/acorn.d.ts", // 类型文件入口
  "module": "dist/acorn.mjs", // esmodule风格导出
  "exports": { // 声明导出
    ".": [
      {
        "import": "./dist/acorn.mjs", // 用import时导出
        "require": "./dist/acorn.js", // 用require时导出
        "default": "./dist/acorn.js" // 默认导出
      },
      "./dist/acorn.js"
    ],
    "./package.json": "./package.json"
  },
  "version": "8.8.0", // 版本(重要)
  "engines": { // 项目运行的版本范围
    "node": ">=0.4.0"
  },
  "repository": { // 源码仓库地址
    "type": "git",
    "url": "https://github.com/acornjs/acorn.git"
  },
  "license": "MIT", // 协议
  "scripts": { // 脚本命令
    "prepare": "cd ..; npm run build:main"
  },
  "devDependencies": { // 开发过程中模块依赖
    "c8": "^7.7.3",
    "chai": "^4.3.4",
    ....
  },
  "dependencies": { // 模块依赖的模块列表
    "ajv": "^6.12.4",
    "debug": "^4.3.2",
    ....
  },
  "bin": { // 可执行模块
    "acorn": "./bin/acorn"
  }
}

通过以上属性我们对三方库有了一个整体的认识,之后不论是通过README.md学习使用方法,还是通过repository地址,直接学习源码,都有迹可循。

包管理器

当我们安装npm库的时候需要借助包管理器的能力。下图概括了执行npm install的流程,接下来我们就拆开来来学习。

image.png

依赖安装结构

我们知道项目依赖的包都安装到了node_modoules文件夹,那么当我们依赖的包本身也依赖其他三方库时,应该安装到什么地方呢?

早期的npm版本,是递归安装,严格安装package.json申明的dependencies列表,在各自的文件夹下面安装依赖。比如会出现这样的文件路径 myapp\node_modules\aggregate-error\node_modules\ndent-string\node_modules\...

这样的好处是逻辑简单并且有稳定的依赖结构。但是当项目依赖逐步增长时,会出现一些问题:

  • 同样的模块会被重复安装,会产生很多小文件,导致我们项目安装依赖非常慢。
  • 依赖嵌套层级过深,在window系统中文件路径最大长度为260个字符,这可能导致不可预知的问题。

所以在npm3之后,嵌套结构优化成了扁平结构

  • npm安装依赖时,不管是自身依赖还是三方库的依赖,都优先安装在顶层node_modoules文件夹中。
  • 当遇到重复安装的模块时,如果版本范围兼容,则直接略过。
  • 当遇到重复安装的模块时,如果版本范围不兼容,则在当前模块的node_modules 下安装。

npm通过以上策略解决了部分嵌套结构的问题,但是又带来了一个新的问题:结构不确定性。

npm安装包时,是按照 package.json 里依赖的顺序依次解析,那么依赖的放置顺序会直接影响最终安装结构,但是我们开发的时候并不知道当前依赖的放置顺序会引起什么变化,这会导致不确定性。

另外,我们在引入三方库的时候一般只会锁定大版本,那么npm安装的时候总会去安装最新版本的包,这会导致其它重复依赖的版本兼容性,同样也导致了不确定性。

相信大家在实际工作中遇到过这种不确定性带来的问题,并且这些问题往往表现非常奇怪也难以排查。

lock文件

为了解决不确定性,npm5新增了package-lock.json 文件。lock文件同时锁定了版本和结构,只要项目里面有lock文件,那么每次安装的结果一定是完全相同的。

{
  "name": "myapp", // 项目名称
  "version": "1.1.0", // 项目版本
  "lockfileVersion": 2, 
  "requires": true,
  "packages": { // 依赖列表
    "node_modules/@commitlint/cli": {
      "version": "7.6.1", // 锁定版本
      "resolved": "https://registry.npmmirror.com/@commitlint/cli/-/cli-7.6.1.tgz", // 引用地址
      "integrity": "sha512-HEJwQ/aK0AOcAwn77ZKbb/GZhlGxBSPhtVp07uoJFVqM12l2Ia2JHA+MTpfHCFdVahKyYGREZgxde6LyKyG8aQ==", // 唯一hash 来验证已安装的软件包是否被改动过、是否已失效
      "dev": true, // 被安装位置
      "dependencies": { // 和package.json一致
        "@commitlint/format": "^7.6.1",
        "@commitlint/lint": "^7.6.0",
        "@commitlint/load": "^7.6.1",
        "@commitlint/read": "^7.6.0",
        "babel-polyfill": "6.26.0",
        "chalk": "2.3.1",
        "get-stdin": "7.0.0",
        "lodash": "4.17.11",
        "meow": "5.0.0",
        "resolve-from": "5.0.0",
        "resolve-global": "1.0.0"
      },
      "bin": {
        "commitlint": "lib/cli.js"
      },
      "engines": {
        "node": ">=4"
      }
    },
    "node_modules/callsites": {
      "version": "3.1.0",
      "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz",
      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
      "dev": true,
      "engines": {
        "node": ">=6"
      }
    },
    "node_modules/caller-callsite": {
      "version": "2.0.0",
      "resolved": "https://registry.npmmirror.com/caller-callsite/-/caller-callsite-2.0.0.tgz",
      "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==",
      "dev": true,
      "dependencies": {
        "callsites": "^2.0.0"
      },
      "engines": {
        "node": ">=4"
      }
    },
    "node_modules/caller-callsite/node_modules/callsites": { // 版本冲突时重复安装
      "version": "2.0.0",
      "resolved": "https://registry.npmmirror.com/callsites/-/callsites-2.0.0.tgz",
      "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==",
      "dev": true,
      "engines": {
        "node": ">=4"
      }
    }
  }
}

除了锁定依赖版本和机构之外,lock文件还可以增加安装速度,因为已经确定了库的版本和下载地址,节省了npm去远程仓库查询的时间。

我们在实际开发项目时,建议把package-lock.json 加入git版本管理,项目组成员统一,这可以减少很多诡异的问题。

缓存策略

在执行npn install时,除了把包安装到项目目录,还会缓存一份到npm-cache目录。通过 npm config get cache 命令可以查询到缓存路径,windowsC:\Users\用户名\AppData\Local\npm-cache

在这个目录下又存在两个目录:content-v2index-v5content-v2 目录用于存储包的具体信息,包括所有版本信息及缓存位置,而index-v5目录用于存储包的 hash

npm 在执行安装时,可以根据 package-lock.json 中存储的 integrity、version、name 生成一个唯一的 key 对应到 index-v5 目录下的缓存记录,然后根据特定算法(具体算法可以看npm源码的tar.js),算出content-v2包的位置。

比如随意找一个index-v5目录里面的proto-list包,可以算出sha512信息:a7df35db45f26cf0c6597404450351e3443905a2705f2eda324452d7dcce9facf35d922a0e66395a33643fc0a749ef9c55c1316c9c10d418c8c7435bd84fd9ae (前四位是目录,后面的是文件名) 对应路径 _cacache\content-v2\sha512\a7\df\35db45f26cf0c6597404450351e3443905a2705f2eda324452d7dcce9facf35d922a0e66395a33643fc0a749ef9c55c1316c9c10d418c8c7435bd84fd9ae文件。

npm 提供了几个命令来管理缓存数据:

  • npm cache clean:删除缓存目录下的所有数据,为了保证缓存数据的完整性,需要加上 --force 参数。
  • npm cache verify:验证缓存数据的有效性和完整性,清理垃圾数据。

基于缓存数据,npm 提供了离线安装模式,分别有以下几种:

  • --prefer-offline:优先使用缓存数据,如果没有匹配的缓存数据,则从远程仓库下载。
  • --prefer-online:优先使用网络数据,如果网络数据请求失败,再去请求缓存数据,这种模式可以及时获取最新的模块。
  • --offline:不请求网络,直接使用缓存数据,一旦缓存数据不存在,则安装失败。

文件完整性

npm下载包到本地后,首先就会校验文件的完整性,确保下载过程中没有错误。

执行npm info lodash我们可以得到

lodash@4.17.21 | MIT | deps: none | versions: 114
Lodash modular utilities.
https://lodash.com/

keywords: modules, stdlib, util

dist
.tarball: https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz
.shasum: 679591c564c3bffaae8454cf0b3df370c3d6911c
.integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==

maintainers:
- mathias <mathias@qiwi.be>
- jdalton <john.david.dalton@gmail.com>
- bnjmnt4n <benjamin@dev.ofcr.se>

dist-tags:
latest: 4.17.21

其中shasum就是完整性hash,包下载到本地后,本地会再计算一次文件的 hash 值,如果两个 hash 值是相同的,则可以确定下载的包是完整的,如果不同,则进行重新下载。

整体流程

整体流程如下:

  • 检查 .npmrc 文件:优先级为:项目级的 .npmrc 文件 > 用户级的 .npmrc 文件> 全局级的 .npmrc 文件 > npm 内置的 .npmrc 文件

  • 检查项目中有无 lock 文件。

  • lock 文件则重新构建依赖树:

    • npm 远程仓库获取包信息

    • 根据 package.json 构建依赖树,构建过程:

      -`npm`安装依赖时,不管是自身依赖还是三方库的依赖,都优先安装在顶层`node_modoules`文件夹中。
      - 当遇到重复安装的模块时,如果版本范围兼容,则直接略过。
      - 当遇到重复安装的模块时,如果版本范围不兼容,则在当前模块的`node_modules` 下安装。   
      

      注意这一步只是确定逻辑上的依赖树,并非真正的安装,后面会根据这个依赖结构去下载或拿到缓存中的依赖包

    • 在缓存中依次查找依赖树中的每个包

      • 不存在缓存:

        • npm 远程仓库下载包

        • 校验包的完整性

        • 校验不通过:

          • 重新下载
        • 校验通过:

          • 将下载的包复制到 npm 缓存目录
          • 将下载的包按照依赖结构解压到 node_modules
      • 存在缓存:将缓存按照依赖结构解压到 node_modules

    • 将包解压到 node_modules

    • 生成 lock 文件

lock 文件:

  • 检查 package.json 中的依赖版本是否和 package-lock.json 中的依赖有冲突。
  • 如果没有冲突,直接跳过获取包信息、构建依赖树过程,开始在缓存中查找包信息,后续过程相同

流程图如下: image.png

yarn

yarn 是在 2016 年发布的,那时 npm 还处于 V3 时期,还没有 package-lock.json 文件,就像上面我们提到的:不稳定性、安装速度慢等缺点经常会受到广大开发者吐槽。

image.png

当时 yarn 的推出,基于以上的优点,快速受到大家欢迎。后来npm多次迭代升级,借鉴了很多yarn的优秀设计,基本上从使用上来说,两者没有太大区别。

yarn安装后也会生成lock文件yarn.lock

# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


"@ampproject/remapping@^2.1.0":
  version "2.2.0"
  resolved "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d"
  integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==
  dependencies:
    "@jridgewell/gen-mapping" "^0.1.0"
    "@jridgewell/trace-mapping" "^0.3.9"

"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.0.0-beta.44", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.8.3":
  version "7.18.6"
  resolved "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
  integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
  dependencies:
    "@babel/highlight" "^7.18.6"

"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8":
  version "7.18.8"
  resolved "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d"
  integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==

"@babel/core@>=7.2.2", "@babel/core@^7.14.0", "@babel/core@^7.14.5", "@babel/core@^7.17.9", "@babel/core@^7.8.7":
  version "7.18.9"
  resolved "https://registry.npmmirror.com/@babel/core/-/core-7.18.9.tgz#805461f967c77ff46c74ca0460ccf4fe933ddd59"
  integrity sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g==
  dependencies:
    "@ampproject/remapping" "^2.1.0"
    "@babel/code-frame" "^7.18.6"
    "@babel/generator" "^7.18.9"
    "@babel/helper-compilation-targets" "^7.18.9"
    "@babel/helper-module-transforms" "^7.18.9"
    "@babel/helpers" "^7.18.9"
    "@babel/parser" "^7.18.9"
    "@babel/template" "^7.18.6"
    "@babel/traverse" "^7.18.9"
    "@babel/types" "^7.18.9"
    convert-source-map "^1.7.0"
    debug "^4.1.0"
    gensync "^1.0.0-beta.2"
    json5 "^2.2.1"
    semver "^6.3.0"

整体上和 package-lock.json 非常类似,区别在于:

  • package-lock.json 使用的是 json 格式,yarn.lock 使用的是一种自定义格式
  • yarn.lock 子依赖的版本号不是固定的,意味着单独又一个 yarn.lock 确定不了 node_modules 目录结构,还需要和 package.json 文件进行配合。而 package-lock.json 只需要一个文件即可确定。

yarn缓存比较直观,每个缓存的模块被存放在独立的文件夹,文件夹名称包含了模块名称、版本号等信息。使用命令 yarn cache dir 可以查看缓存数据的目录:

image.png

yarn 默认使用 prefer-online 模式,即优先使用网络数据,如果网络数据请求失败,再去请求缓存数据。

lock文件更新策略

版本冲突引起更新

  • npm 5.0.x 版本:不管 package.json 中依赖是否有更新,npm i 都会根据 package-lock.json 下载。针对这种安装策略,有人提出了这个 issue - #16866 ,然后就演变成了 5.1.0 版本后的规则。

  • 5.1.0 版本后:当 package.json 中的依赖项有新版本时,npm install 会无视 package-lock.json 去下载新版本的依赖项并且更新 package-lock.json。针对这种安装策略,又有人提出了一个 issue - #17979 ,参考 npm 贡献者 iarna 的评论,得出 5.4.2 版本后的规则。

  • 5.4.2 版本后:

    • 如果只有一个 package.json 文件,运行 npm i 会根据它生成一个 package-lock.json 文件。
    • 如果 package.json 的 semver-range version 和 package-lock.json 中版本兼容,即使此时 package.json 中有新的版本,执行 npm i 也还是会根据 package-lock.json 下载。
    • 如果手动修改了 package.json 的 version ranges,且和 package-lock.json 中版本不兼容,那么执行 npm i 时 package-lock.json 将会更新到兼容 package.json 的版本。

resolved字段引起更新

{ 
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", 
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, 
"engines": { 
    "node": ">=6" 
    }
}

resolved是记录包引用地址的字段,这本来没有什么问题,但由于国内网络环境,开发中实际使用的registry地址,一般是使用淘宝代理的npm源地址或者公司私有的源地址,这会造成一些问题。

  • 如果项目组每个人的npm源地址设置不同,生成的lock文件也会是五花八门的源地址。
  • 当项目组成员使用另外的地址安装npm包后,所有重新安装的包会更新resolved字段,造成代码冲突。

所以推荐项目里面添加.npmrc文件,统一npm相关配置。

registry=https://registry.npm.taobao.org/ // 统一使用淘宝源地址
@private:registry = https://npm.xx.com/  // @private开头的公司私有库使用私有地址

提升依赖安装速度

不得不说每次重新安装依赖都是一个痛苦的过程,我只能盯着屏幕无助的等待。(当然这也是一段愉快的摸鱼时间,每次老板看到我耍手机,我都说在安装依赖或等编译。😂 )那么有哪些手段能提升我们的安装速度呢?

使用私有镜像仓库

在大型的项目中,一次安装过程往往有几千次的网路请求,包括远程仓库版本查询,tar包下载等。那么使用国内的公有镜像库是一个不错的选择,如淘宝镜像,它每隔十分钟与官方镜像仓库同步一次,而它位于国内,网络不至于太慢。

另外一种选择是企业内搭建一套私有仓库,定时去同步官方镜像。这样本地局域网就闭环了所有操作,速度起飞。

充分利用缓存

虽然我们每次安装依赖时,包管理器都会把文件写一份到缓存中,但再次安装时,缓存优先级并不是想象中那么高。比如yarn 默认使用 prefer-online 模式,即优先使用网络数据,如果网络数据请求失败,再去请求缓存数据。这时候本地缓存仅是作为错误兜底。

为了只痛苦一次,我们可以使用--prefer-offline 来指定缓存优先策略,充分利用缓存。

使用 npm ci 替代 npm i

npm ci 是为了适用于 CI 环境而推出的命令,它做了一系列优化,如去除掉一些面向用户的特性来加强速度。除了性能,它也有一些在 CI 上基于完整性与安全性的检查,如 package.json 与 package-lock.json 版本不一致的问题。

为了更好地提高速度,npm ci 基于一个独立的库 libcipm (opens new window)安装依赖,而它拥有和 npm install 兼容的 API。并且当它安装依赖时,默认是缓存优先的,它会充分利用缓存,从而加速装包。

经实验,npm ci 可以减少将近一半的的依赖安装时间。

image.png

上图是我找到的一张优化效果对比图,大家可以感受下。

pnpm

目前来说,yarnnpm区别不大,实际工作中用哪个都可以,只要团队内统一就行。那目前市面上有没有更好用的包管理器呢? 接下来让我们一起了解一下pnpm

pnpm代表performant npm(高性能的npm),官网上介绍为快速的,节省磁盘空间的包管理工具

使用上和npm语法相似,比如常用的pnpm initpnpm install xxxpnpm run xxx等,很快能上手。另外pnpm还可以管理node环境。(可实现nvmnnode版本管理工具,安装并切换node.js版本的功能。)

  • 本地安装并使用:pnpm env use <node版本号>
  • 全局安装并使用:pnpm env use --global <node版本号>

速度提升

基于pnpm优秀的设计,它在安装速度上有明显的改善,大多数情况下pnpm 比其他包管理器快 2 倍

pnpm官网上,提供了一个benchmarks基准测试图表,它展示了npmpnpmYarnYarn pnpinstallupdate等场景下的耗时:

image.png

image.png

节约磁盘空间

image.png

当使用 npmYarn 时,如果你有 100 个项目使用了某个依赖(dependency),就会有 100 份该依赖的副本保存在硬盘上。 而在使用 pnpm 时,依赖会被存储在内容可寻址的存储中,所以:

  1. 如果你用到了某依赖项的不同版本,只会将不同版本间有差异的文件添加到仓库。 例如,如果某个包有100个文件,而它的新版本只改变了其中1个文件。那么 pnpm update 时只会向存储中心额外添加1个新文件,而不会因为仅仅一个文件的改变复制整新版本包的内容。
  2. 所有文件都会存储在硬盘上的某一位置。 当软件包被被安装时,包里的文件会硬链接到这一位置,而不会占用额外的磁盘空间。 这允许你跨项目地共享同一版本的依赖。

因此,您在磁盘上节省了大量空间,这与项目和依赖项的数量成正比,并且安装速度要快得多!

稳定

之前我们提到在npm3之后,嵌套结构优化成了扁平结构。这会造成两个问题:

  1. 由于所有包都被提升到模块目录的根目录,项目可以访问到未被添加进当前项目的依赖而不会报错。
  2. 扁平结构是不稳定的,会根据安装顺序不同,实际依赖结构不同,需要借助lock文件规避这个问题。

默认情况下,pnpm 使用软链的方式将项目的直接依赖添加进模块文件夹的根目录,项目中node_modules文件夹下只会有直接依赖的软连接,直接依赖的次级依赖放在自本身的node_modules文件夹下,所以pnpm生成的是稳定的嵌套结构。

对比npmpnpm安装的node_modules

npmpnpm
image.pngimage.png
所有依赖包平铺在node_modules目录,包括直接依赖包以及其他次级依赖包node_modules目录下只有.pnpm和直接依赖包(vue、vite、...),没有其他次级依赖包
没有符号链接直接依赖包的后面有符号链接的标识

npm或Yarn 转 pnpm

pnpm有这些优点,如果我想从npmyarn转到pnpm,应该怎么做影响最小呢?

  1. 全局安装pnpm
npm install -g pnpm
  1. 删除npmyarn生成的node_modules
# 项目目录下运行或手动物理删除
rm -rf node_modules
  1. pnpm import从其他软件包管理器的lock 文件生成 pnpm-lock.yaml,再执行pnpm install --frozen-lockfile(相当于npm ci)生成依赖,防止没有lock文件意外升级依赖包,导致项目出错
# 生成`pnpm-lock.yaml`
pnpm import

# 安装依赖
pnpm install --frozen-lockfile
  1. 删除npmyarn生成的lock文件
# 删除package-lock.json
rm -rf package-lock.json
# 删除yarn.lock
rm -rf yarn.lock
  1. 项目中的npm命令等修改为pnpm,包括README文档、运行命令等

整体来看,pnpm是值得我们去尝试、去学习的提升效能的工具,但是工作项目中是否使用或者切换,应该根据实际情况来判断,毕竟项目稳定后,安装依赖的频次是很低的。

引用致谢:

  1. npm install 原理分析
  2. npm install package-lock.json 的更新策略
  3. 在生产环境中使你的 npm i 速度提升 50%
  4. 2022年了,你还没用pnpm吗?