npm & yarn & pnpm 包管理机制

1,332 阅读38分钟

npm

一、从内部机制和核心原理了解npm

1.npm 的安装机制和背后思想

image.png

  • npm install 执行之后,首先,检查并获取 npm 配置
    这里的优先级为:项目级的.npmrc文件 > 用户级的.npmrc 文件 > 全局级的 .npmrc 文件 > npm 内置的 .npmrc 文件

  • 检查项目中是否有 package-lock.json 文件

    • 若有:检查 package-lock.jsonpackage.json 中声明的依赖是否一致:一致就直接使用package-lock.json 中的信息,从缓存或网络资源中加载依赖;不一致,则依据npm的版本进行处理
    • 若无:则根据 package.json 递归构建依赖树,按照构建好的依赖树下载完整的依赖资源,在下载时就会检查是否存在相关资源缓存:存在缓存,就会将缓存内容解压到node_modules 中;不存在缓存,就从npm远程仓库下载包,并会检验包的完整性,随后添加到缓存,解压到node_modules 中生成package-lock.json
  • 最后生成 package-lock.json

    • npmV5.0.x是根据package-lock.json下载
    • npmV5.1.0-5.4.2:当package.json声明的依赖版本规范有符合的更新版本时,会按照package.json安装,并更新package-lock.json
    • npmV5.4.2以上,当package.json声明的依赖版本规范和package-lock.json安装版本兼容,则按照package-lock.json安装,如果不兼容,按照package.json安装,并更新package-lock.json

这里值得注意的是:npm不同版本在检查 package-lock.json 和 package.json 中声明的依赖是否一致的时候处理是不一样的。

npm 安装依赖大致的过程,其中这样几个步骤需要关注:

  1. 检查配置。  包括项目级、用户级、全局级、内置的  .npmrc 文件。
  2. 确定依赖版本,构建依赖树。  确定项目依赖版本有两个来源,一是 package.json 文件,一是 lockfile 文件,两个确认版本、构建依赖树的来源,互不可少、相辅相成。如果 package-lock.json 文件存在且符合 package.json 声明的的情况下,直接读取;否则重新确认依赖的版本。
  3. 下载包资源。  下载前先确认本地是否存在匹配的缓存版本,如果有就直接使用缓存文件,如果没有就下载并添加到缓存,然后将包按依赖树解压到 node_modules 目录。
  4. 生成 lockfile 文件。

可以确认这样几个逻辑:

  1. 构建依赖树的过程中,版本确认需要结合 package.json 和 package-lock.json 两个文件。先确认 package-lock.json 安装版本,符合规则就以此为准,否则由 package.json 声明的版本范围重新确认。特别地,若是在开发中手动更改包信息,会导致 lockfile 版本信息异常,也可能由 package.json 确认。确认好的依赖树会存到 package-lock.json 文件中,这里跟 yarn.lock 存在差异。
  2. 同一个依赖,更高版本的包会安装到顶层目录,即 node_modules 目录;否则会分散在某些依赖的 node_modules 目录,如:node_modules/expect-jsx/node_modules/react 目录。
  3. 如果依赖升级,造成版本不兼容,需要多版本共存,那么仍然是将高版本安装到顶层,低版本分散到各级目录。
  4. lockfile 的存在,保证了项目依赖结构的确定性,保障了项目在多环境运行的稳定性。

2.lock文件

上面说到了npm install 的时候会检查项目中是否有 package-lock.json 文件,Lock文件的出现是为了解决 npm install 的不确定性问题 ,在 npm 5.x 版本新增了 package-lock.json 文件。

它的作用是锁定依赖结构,只要你目录下有 package-lock.json 文件,那么你每次执行 npm install 后生成的 node_modules 目录结构一定是完全相同的。

文件说明:

  • version: 包版本,这个包当前安装在 node_modules 中的版本
  • resolved: 包具体的安装来源
  • integrity:包 `` 值,基于 Subresource Integrity 来验证已安装的软件包是否被改动过、是否已失效源
  • requires: 对应子依赖的依赖,与子依赖的 package.jsondependencies的依赖项相同
  • dependencies: 结构和外层的 dependencies 结构相同,存储安装在子依赖 node_modules 中的依赖包。

这里注意,并不是所有的子依赖都有 dependencies 属性,只有子依赖的依赖和当前已安装在根目录的 node_modules 中的依赖冲突之后,才会有这个属性。

3.node_modules 中模块目录结构

嵌套结构

在早期,npm3.x之前,是嵌套结构,就是我们开篇问的我们的应用依赖了公共库 A 和公共库 B,同时公共库 A 也依赖了公共库 B,npm3.x之前公共库 B 会被多次安装。结构如图: image.png

这样的方式优点很明显, node_modules 的结构和 package.json 结构一一对应,层级结构明显,并且保证了每次安装目录结构都是相同的。

但是,试想一下,如果你依赖的模块非常之多,你的 node_modules 将非常庞大,嵌套层级非常之深:

  • 在不同层级的依赖中,可能引用了同一个模块,导致大量冗余。
  • Windows 系统中,文件路径最大长度为260个字符,嵌套层级过深可能导致不可预知的问题

扁平结构

为了解决以上问题,NPM3.x 版本做了一次较大更新。其将早期的嵌套结构改为扁平结构: 安装模块时,不管其是直接依赖还是子依赖的依赖,优先将其安装在 node_modules 根目录。还是上面的依赖结构,我们在执行 npm install 后将得到下面的目录结构。

image.png

image.png

对应的,如果我们在项目代码中引用了一个模块,模块查找流程如下:

  1. 在当前模块路径下搜索
  2. 在当前模块 node_modules 路径下搜素
  3. 在上级模块的 node_modules 路径下搜索
  4. 直到搜索到全局路径中的 node_modules

但是有一种情况是,你的应用中,没有直接依赖公共库D,比如公共库C和公共库B依赖了不同版本的公共库D

image.png

所以 npm 3.x 版本并未完全解决老版本的模块冗余问题,甚至还会带来新的问题,因为在执行 npm install 的时候,按照 package.json 里依赖的顺序依次解析,公共库B和公共库C在 package.json的放置顺序则决定了 node_modules 的依赖结构。

4.npm 缓存机制

在执行 npm install 或 npm update命令下载依赖后,除了将依赖包安装在node_modules 目录下外,还会在本地的缓存目录缓存一份。

通过npm config get cache 命令可以查询到:npm-cache 目录

image.png

image.png

  • index-v5 文件中,存储tar包的 hash

npm 在执行安装时,可以根据 package-lock.json 中存储的 integrityversionname 生成一个唯一的 key 对应到 index-v5 目录下的缓存记录,从而找到 tar包的 hash,然后根据 hash 再去找缓存的 tar包,通过pacote对应的二进制文件解压到对应项目的 node_modules 下面,省去了网络下载资源的开销。

注意:以上的缓存策略是从 npm v5 版本开始的,在 npm v5 版本之前,每个缓存的模块在 ~/.npm 文件夹中以模块名的形式直接存储,储存结构是{cache}/{name}/{version}

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

  • npm cache add: 官方解释说这个命令主要是 npm 内部使用,但是也可以用来手动给一个指定的 package 添加缓存。
  • npm cache clean: 删除缓存目录下的所有数据,为了保证缓存数据的完整性,需要加上 --force 参数
  • npm cache verify: 验证缓存数据的有效性和完整性,清理垃圾数据

5.npx

npxnpm v5.2 版本引入,解决了 npm 的一些使用快速开发、调试,以及项目内使用全局模块的痛点。

npx 执行模块时会优先安装依赖,但是在安装执行后便删除此依赖,这就避免了全局安装模块带来的问题。

使用代码检测工具 ESLint比较方便

  • npx eslint --init
  • npx eslint yourfile.js

6.npm常用命令

npm init:初始化一个新的npm项目,跳过npm init命令行接口(CLI)。
npm install:根据项目中的package.json文件自动下载项目所需的全部依赖。
npm install 包名 --save-dev:安装的包只用于开发环境,不用于生产环境,会出现在package.json文件中的dependencies属性中。
npm install 包名 --save:安装的包需要发布到生产环境的,会出现在package.json文件中的dependencies属性中。
npm list:查看当前目录下已安装的node包。
npm list -g:查看全局已经安装过的node包。
npm update 包名:更新指定包。
npm uninstall 包名:卸载指定包。
npm config list:查看配置信息。
npm info 包名:查看包的详细信息。
npm search 字符串/正则表达式:搜索npm仓库。
npm logout:退出npm的登录状态。
npm login:登录npm,输入用户名和密码。
npm whoami:查看当前登录的用户名。
npm cache clean:清理npm缓存。
npm cache verify:检查npm缓存的有效性。
npm dist-tag 标签名 包名:给包添加或修改发布标签。
npm tag 包名 标签名:给包添加或修改标签。
npm deprecate 包名 消息:给包发布废弃警告。
npm run-script 脚本名:运行在package.json中定义的脚本。
npm doc 包名:查看指定包的文档。
npm home 包名:查看指定包的官方文档首页。
npm bug 包名:查看指定包的bug列表。
npm search 关键字:搜索npm仓库中的包。
npm view 包名 字段 [值]**:查看包的指定字段值。
npm view 包名 version:查看指定包的版本号。
npm view 包名 engines:查看指定包支持的Node.js版本。
npm dedupe 包名:消除包之间的重复依赖。
npm version minor:更新包的版本号为次要版本。
npm version major:更新包的版本号为主要版本。
npm tag 包名@版本号:给指定版本的包添加标签。
npm publish 包名:发布包到npm仓库。
npm adduser:添加用户到npm仓库。
npm config get 字段名:获取npm配置的字段值。
npm config set 字段名 值:设置npm配置的字段值。
npm config delete 字段名:删除npm配置的字段值。
npm config list:列出npm的所有配置。
npm config edit:编辑npm的配置文件。
npm config merge 合并文件:合并其他配置文件到当前配置文件。
npm config set always-auth true:设置npm始终进行身份验证。
npm config unset always-auth:取消设置npm始终进行身份验证。
npm unpublish 包名:从npm仓库中删除指定的包。
npm config get always-auth:获取npm始终进行身份验证的设置。
npm config set always-auth false:取消设置npm始终进行身份验证。
npm config set email [email]**:设置npm的邮箱地址。
npm config set username [username]**:设置npm的用户名。
npm config set always-fetch true:设置npm始终从服务器获取最新的包。
npm config set always-fetch false:取消设置npm始终从服务器获取最新的包。
npm config set prepublishOnly true:设置只在预发布时运行脚本。
npm config set prepublishOnly false:取消只在预发布时运行脚本的设置。
npm config set script-shell [shell]**:设置用于执行脚本的shell。
npm config set strict-ssl [boolean]**:设置是否启用严格的SSL验证。
npm config rm username:删除npm的用户名配置。
npm config rm email:删除npm的邮箱地址配置。
npm config rm always-auth:删除npm始终进行身份验证的设置。
npm config rm always-fetch:删除npm始终从服务器获取最新的包的设置。
npm config rm prepublishOnly:删除只在预发布时运行脚本的设置。
npm config rm script-shell:删除用于执行脚本的shell的设置。
npm config rm proxy:删除npm的代理服务器设置。
npm config rm https-proxy:删除npm的HTTPS代理服务器设置。
npm config rm http-proxy:删除npm的HTTP代理服务器设置。
npm config rm no-proxy:删除npm的不使用代理服务器的设置。
npm config rm logfile [filepath]**:设置npm的日志文件路径。
npm config rm loglevel [level]**:设置npm的日志级别。
npm config rm save-prefix [prefix]**:设置npm保存包前缀。
npm config rm save-exact [boolean]**:设置是否保存确切版本。
npm config rm always-audit [boolean]**:设置是否总是进行安全审计。
npm config rm audit [boolean]**:设置是否进行安全审计。
npm config rm audit-update [boolean]**:设置是否在更新时进行安全审计。
npm config rm force [boolean]**:设置是否强制覆盖已存在的包。
npm config rm global [boolean]**:设置是否全局安装包。
npm config rm unsafe-perm [boolean]**:设置是否使用不安全的权限安装包。
npm config rm平民化出版,知识传播更简单,更直接,更高效!:删除npm的平民化出版设置。
npm config rm平民化出版-global [boolean]**:设置是否全局发布平民化出版包。
npm config rm平民化出版-audit [boolean]**:设置是否进行平民化出版安全审计。
npm config rm平民化出版-* [pattern]**:删除与平民化出版相关的所有未标记的配置。
npm config rm平民化出版-* [pattern] --* [pattern]**:删除与平民化出版相关的所有标记的配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern]。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern]。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern]。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]。:删除与平民化出版相关的特定配置。
npm config edit:在默认编辑器中打开用户配置文件。
npm config env:打印出配置相关的环境变量。
npm config list:列出所有的配置选项。
npm config diff:显示当前包和默认配置之间的差异。
npm config set平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]**:设置与平民化出版相关的特定配置。
npm config edit --global:在全局配置文件中打开用户配置文件。
npm config diff **:显示指定的包和默认配置之间的差异。
npm config list [pattern]**:列出所有包含指定模式的配置选项。
npm config edit --merge:在当前包配置文件中打开用户配置文件并合并已定义的属性。
npm config rm **:删除指定的包配置选项。
npm config mv  **:将指定的包配置选项重命名。
npm config unset **:删除指定的包配置选项。
npm config get **:获取指定的配置选项的值。
npm config set  **:设置指定的配置选项的值。
npm config default:恢复到默认的npm配置选项。
npm config rm  --global:删除全局指定的包配置选项。
npm config mv   --global:将全局指定的包配置选项重命名。
npm config unset  --global:删除全局指定的包配置选项。
npm config get  --global:获取全局指定的配置选项的值。
npm config set   --global:设置全局指定的配置选项的值。
npm config ls [-l]**:列出当前包的所有配置选项。
npm config ls-all [-l]**:列出所有包的所有配置选项。
npm config edit-json:在默认编辑器中打开用户配置的JSON文件。
npm config diff  --json:以JSON格式显示指定的包和默认配置之间的差异。
npm config ls-files:列出已追踪的包含特定配置的文件的名称。
npm config ls-all-user:列出所有用户配置选项。
npm config ls-global:列出全局配置选项。
npm config ls-org:列出特定组织/公司的所有配置选项。
npm config ls-prefix:列出特定前缀的所有配置选项。
npm config set [value]**:设置特定的用户配置选项。
npm config unset [name]**:删除特定的用户配置选项。
npm config view [name]**:以可读的方式显示特定的配置选项的值。
npm config edit [name]**:在默认编辑器中打开用户配置文件并编辑特定的配置选项。
npm config init [--user=false]**:初始化一个空的npm配置文件。
npm config save [--global] [--save-to=package.json] [--save-prefix="] [--save-exact=false] [--save-bundle=false]**:将当前配置选项保存到包中。
npm config edit --global:在全局配置文件中打开用户配置文件并编辑特定的配置选项。
npm config get-or-set:获取或设置配置选项的值。
npm config edit [--global]**:在默认编辑器中打开用户配置文件并编辑所有的配置选项。
npm config get-bool [--global] **:获取布尔类型配置选项的值。
npm config get-func [--global] **:获取函数类型配置选项的值。
npm config get-string [--global] **:获取字符串类型配置选项的值。
npm config get-number [--global] **:获取数字类型配置选项的值。
npm config get-array [--global] **:获取数组类型配置选项的值。
npm config get-object [--global] **:获取对象类型配置选项的值。
npm config get-url [--global] **:获取URL类型配置选项的值。
npm config set-url [--global]  **:设置URL类型配置选项的值。
npm config ls-default-values:列出npm默认的配置选项值。
npm config rm-context:删除当前的npm上下文。
npm config describe **:获取描述信息关于配置选项。
npm config set-json [--global]  **:设置JSON类型的配置选项的值。
npm config unset-json [--global] **:删除JSON类型的配置选项。
npm config get-json [--global] **:获取JSON类型的配置选项的值。
npm config set =**:设置特定的配置选项的值。
npm config output **:以列表形式显示与给定模式匹配的所有配置选项的值。
npm config unset  [--global]**:删除特定的配置选项。
npm config set-script  **:设置一个脚本的配置选项的值。
npm config unset-script **:删除一个脚本的配置选项。
npm config get-script **:获取一个脚本的配置选项的值。
npm config list:列出所有的配置选项及其值。
npm config delete **:删除特定的配置选项。
npm config get-stream [--global] **:将配置选项的值输出到标准输出流中。
npm config set-stream [--global]  **:将配置选项的值设置为从标准输入流中接收的值。

按照字母排序

npm adduser:添加用户到npm仓库。
npm bug 包名:查看指定包的bug列表。
npm cache clean:清理npm缓存。
npm cache verify:检查npm缓存的有效性。
npm config default:恢复到默认的npm配置选项。
npm config delete **:删除特定的配置选项。
npm config delete 字段名:删除npm配置的字段值。
npm config describe **:获取描述信息关于配置选项。
npm config diff  --json:以JSON格式显示指定的包和默认配置之间的差异。
npm config diff **:显示指定的包和默认配置之间的差异。
npm config diff:显示当前包和默认配置之间的差异。
npm config edit --global:在全局配置文件中打开用户配置文件。
npm config edit --global:在全局配置文件中打开用户配置文件并编辑特定的配置选项。
npm config edit --merge:在当前包配置文件中打开用户配置文件并合并已定义的属性。
npm config edit [--global]**:在默认编辑器中打开用户配置文件并编辑所有的配置选项。
npm config edit [name]**:在默认编辑器中打开用户配置文件并编辑特定的配置选项。
npm config edit-json:在默认编辑器中打开用户配置的JSON文件。
npm config edit:在默认编辑器中打开用户配置文件。
npm config edit:编辑npm的配置文件。
npm config env:打印出配置相关的环境变量。
npm config get  --global:获取全局指定的配置选项的值。
npm config get **:获取指定的配置选项的值。
npm config get always-auth:获取npm始终进行身份验证的设置。
npm config get 字段名:获取npm配置的字段值。
npm config get-array [--global] **:获取数组类型配置选项的值。
npm config get-bool [--global] **:获取布尔类型配置选项的值。
npm config get-func [--global] **:获取函数类型配置选项的值。
npm config get-json [--global] **:获取JSON类型的配置选项的值。
npm config get-number [--global] **:获取数字类型配置选项的值。
npm config get-object [--global] **:获取对象类型配置选项的值。
npm config get-or-set:获取或设置配置选项的值。
npm config get-script **:获取一个脚本的配置选项的值。
npm config get-stream [--global] **:将配置选项的值输出到标准输出流中。
npm config get-string [--global] **:获取字符串类型配置选项的值。
npm config get-url [--global] **:获取URL类型配置选项的值。
npm config init [--user=false]**:初始化一个空的npm配置文件。
npm config list [pattern]**:列出所有包含指定模式的配置选项。
npm config list:列出npm的所有配置。
npm config list:列出所有的配置选项。
npm config list:列出所有的配置选项及其值。
npm config list:查看配置信息。
npm config ls [-l]**:列出当前包的所有配置选项。
npm config ls-all [-l]**:列出所有包的所有配置选项。
npm config ls-all-user:列出所有用户配置选项。
npm config ls-default-values:列出npm默认的配置选项值。
npm config ls-files:列出已追踪的包含特定配置的文件的名称。
npm config ls-global:列出全局配置选项。
npm config ls-org:列出特定组织/公司的所有配置选项。
npm config ls-prefix:列出特定前缀的所有配置选项。
npm config merge 合并文件:合并其他配置文件到当前配置文件。
npm config mv   --global:将全局指定的包配置选项重命名。
npm config mv  **:将指定的包配置选项重命名。
npm config output **:以列表形式显示与给定模式匹配的所有配置选项的值。
npm config rm  --global:删除全局指定的包配置选项。
npm config rm **:删除指定的包配置选项。
npm config rm always-audit [boolean]**:设置是否总是进行安全审计。
npm config rm always-auth:删除npm始终进行身份验证的设置。
npm config rm always-fetch:删除npm始终从服务器获取最新的包的设置。
npm config rm audit [boolean]**:设置是否进行安全审计。
npm config rm audit-update [boolean]**:设置是否在更新时进行安全审计。
npm config rm email:删除npm的邮箱地址配置。
npm config rm force [boolean]**:设置是否强制覆盖已存在的包。
npm config rm global [boolean]**:设置是否全局安装包。
npm config rm http-proxy:删除npm的HTTP代理服务器设置。
npm config rm https-proxy:删除npm的HTTPS代理服务器设置。
npm config rm logfile [filepath]**:设置npm的日志文件路径。
npm config rm loglevel [level]**:设置npm的日志级别。
npm config rm no-proxy:删除npm的不使用代理服务器的设置。
npm config rm prepublishOnly:删除只在预发布时运行脚本的设置。
npm config rm proxy:删除npm的代理服务器设置。
npm config rm save-exact [boolean]**:设置是否保存确切版本。
npm config rm save-prefix [prefix]**:设置npm保存包前缀。
npm config rm script-shell:删除用于执行脚本的shell的设置。
npm config rm unsafe-perm [boolean]**:设置是否使用不安全的权限安装包。
npm config rm username:删除npm的用户名配置。
npm config rm-context:删除当前的npm上下文。
npm config rm平民化出版-audit [boolean]**:设置是否进行平民化出版安全审计。
npm config rm平民化出版-global [boolean]**:设置是否全局发布平民化出版包。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]。:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern]。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern]**:删除与平民化出版相关的特定配置。
npm config rm平民化出版-* [pattern] --* [pattern] --* [pattern]。
npm config rm平民化出版-* [pattern] --* [pattern]**:删除与平民化出版相关的所有标记的配置。
npm config rm平民化出版-* [pattern] --* [pattern]。
npm config rm平民化出版-* [pattern]**:删除与平民化出版相关的所有未标记的配置。
npm config rm平民化出版,知识传播更简单,更直接,更高效!:删除npm的平民化出版设置。
npm config save [--global] [--save-to=package.json] [--save-prefix="] [--save-exact=false] [--save-bundle=false]**:将当前配置选项保存到包中。
npm config set   --global:设置全局指定的配置选项的值。
npm config set  **:设置指定的配置选项的值。
npm config set =**:设置特定的配置选项的值。
npm config set [value]**:设置特定的用户配置选项。
npm config set always-auth false:取消设置npm始终进行身份验证。
npm config set always-auth true:设置npm始终进行身份验证。
npm config set always-fetch false:取消设置npm始终从服务器获取最新的包。
npm config set always-fetch true:设置npm始终从服务器获取最新的包。
npm config set email [email]**:设置npm的邮箱地址。
npm config set prepublishOnly false:取消只在预发布时运行脚本的设置。
npm config set prepublishOnly true:设置只在预发布时运行脚本。
npm config set script-shell [shell]**:设置用于执行脚本的shell。
npm config set strict-ssl [boolean]**:设置是否启用严格的SSL验证。
npm config set username [username]**:设置npm的用户名。
npm config set 字段名 值:设置npm配置的字段值。
npm config set-json [--global]  **:设置JSON类型的配置选项的值。
npm config set-script  **:设置一个脚本的配置选项的值。
npm config set-stream [--global]  **:将配置选项的值设置为从标准输入流中接收的值。
npm config set-url [--global]  **:设置URL类型配置选项的值。
npm config set平民化出版-* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern] --* [pattern]**:设置与平民化出版相关的特定配置。
npm config unset  --global:删除全局指定的包配置选项。
npm config unset  [--global]**:删除特定的配置选项。
npm config unset **:删除指定的包配置选项。
npm config unset [name]**:删除特定的用户配置选项。
npm config unset always-auth:取消设置npm始终进行身份验证。
npm config unset-json [--global] **:删除JSON类型的配置选项。
npm config unset-script **:删除一个脚本的配置选项。
npm config view [name]**:以可读的方式显示特定的配置选项的值。
npm dedupe 包名:消除包之间的重复依赖。
npm deprecate 包名 消息:给包发布废弃警告。
npm dist-tag 标签名 包名:给包添加或修改发布标签。
npm doc 包名:查看指定包的文档。
npm home 包名:查看指定包的官方文档首页。
npm info 包名:查看包的详细信息。
npm init:初始化一个新的npm项目,跳过npm init命令行接口(CLI)。
npm install 包名 --save-dev:安装的包只用于开发环境,不用于生产环境,会出现在package.json文件中的dependencies属性中。
npm install 包名 --save:安装的包需要发布到生产环境的,会出现在package.json文件中的dependencies属性中。
npm install:根据项目中的package.json文件自动下载项目所需的全部依赖。
npm list -g:查看全局已经安装过的node包。
npm list:查看当前目录下已安装的node包。
npm login:登录npm,输入用户名和密码。
npm logout:退出npm的登录状态。
npm publish 包名:发布包到npm仓库。
npm run-script 脚本名:运行在package.json中定义的脚本。
npm search 关键字:搜索npm仓库中的包。
npm search 字符串/正则表达式:搜索npm仓库。
npm tag 包名 标签名:给包添加或修改标签。
npm tag 包名@版本号:给指定版本的包添加标签。
npm uninstall 包名:卸载指定包。
npm unpublish 包名:从npm仓库中删除指定的包。
npm update 包名:更新指定包。
npm version major:更新包的版本号为主要版本。
npm version minor:更新包的版本号为次要版本。
npm view 包名 engines:查看指定包支持的Node.js版本。
npm view 包名 version:查看指定包的版本号。
npm view 包名 字段 [值]**:查看包的指定字段值。
npm whoami:查看当前登录的用户名。

二、NPM镜像源

1.npm 中的源(registry)

npm 中的源(registry),其实就是一个查询服务。以 npmjs.org 为例,它的查询服务网址是 registry.npmjs.org/ 这个网址后面跟上模块名,就会得到一个 JSON 对象,里面是该模块所有版本的信息。

在日常开发中,我们也常常会 发现直接用 npm 安装库有时候非常慢,容易安装失败,因为npm安装插件是从国外服务器下载,受网络的影响比较大,可能会出现异常。最好的解决方案是使用淘宝镜像 cnpm替代npm,正如它官网所描述。

这是一个完整 npmjs.org 镜像,你可以用此代替官方版本(只读),同步频率目前为 10分钟 一次以保证尽量与官方服务同步。

我们可以直接全局使用cnpm命令行工具代替默认的npm

  • npm install -g cnpm --registry=https://registry.npmmirror.com

当然你可能不喜欢用cnpm命令,为了方便开发,我们可以把npm的源设置为淘宝镜像

  • $ npm config set registry http://registry.npmmirror.com

查看npm镜像源

  • npm get registry

2.npm镜像管理工具nrm

nrm(npm registry manager )是npm的镜像源管理工具。它可以快速在让你在本地源之间切换。

  • nrm安装
npm install -g nrm
  • 查看当前可使用的镜像源
nrm ls
  • 切换镜像源 使用 taobao 镜像源
nrm use taobao

三、NPM 私有仓库建设指南

1.为什么要搭建npm私有库

当我们业务规模达到一定程度的时候,可能会有一些自己公司私有的的UI的封装或者模块封装,不想通过npm发布,但是又想在一些项目和其他团队共享使用,而且我们不想将公共模块是复制到各个项目中来使用。这时候搭建NPM私有库就显得很有必要。

  • npm私有库只针对公司内部局域网开放,作为内部前端资产,不对外公开,也算是公司前端团队的一个技术亮点
  • 速度比在直接在npm下载更快,甚至是比淘宝源更快,私有库能够将包资源进行缓存,响应的话会加快下载速度
  • 对于发布和下载npm包配置权限管理

2.Verdaccio搭建私有npm仓库

搭建npm私有库的方式很多,笔者推荐一款简单的轻量级私有 NPM 仓库(verdaccio.org)

它的原理就是我们平时使用npm publish进行发布时,上传的仓库默认地址是npm,通过Verdaccio工具在本地新建一个仓库地址,再把本地的默认上传仓库地址切换到本地仓库地址即可。当npm install时没有找到本地的仓库,则Verdaccio默认配置中会从npm中央仓库下载。当然它还可以设置钉钉机器人推送发布消息呢!

具体安装部署细节可以移步到Verdaccio官网

3.社区上主要有3种工具来搭建

npm 私服:nexus、verdaccio 以及 cnpm。

四、项目中NPM

1. NPM 实操建议

(1)推荐使用 npm v5.4.2 以上的 npm 版本,享受npm 的最基本先进性和稳定性。

(2)项目的第一次搭建使用 npm install 安装依赖包,并提交 package.json、package-lock.json,当然要排除 node_modules 目录。

(3)其他项目成员首次 checkout/clone 项目代码后,执行一次 npm install 安装依赖包。

(4)对于升级依赖包的需求:

依靠 npm update 命令升级到新的小版本;

依靠 npm install @ (指定版本)升级大版本;

也可以手动修改 package.json 中版本号,并执行 npm install 来升级版本;

本地验证升级后新版本无问题(比如element各个版本有些不兼容的情况)

(5)对于降级依赖包的需求:执行 npm install @ (指定版本)命令,指定版本

(6)删除项目中某些依赖:执行 npm uninstall 命令, 或者手动操作 package.json删除依赖。

(7)任何团队成员提交 package.json、package-lock.json 更新后,其他成员应该拉取代码后,执行 npm install 更新依赖。

(8)如果 package-lock.json 出现冲突或问题,建议将本地的 package-lock.json 文件删除,引入远程的 package-lock.json 文件和 package.json,再执行 npm install 命令。

2.报错集合

(1) node_modules 删除无权限

解决办法: 通过npm命令来删除node_modules文件夹

  • 1.首先通过npm安装删除工具
npm install rimraf -g
  • 2.安装淘宝镜像后可以使用
cnpm install rimraf -g   命令行语句,速度会更快
  • 3.使用删除命令
rimraf node_modules

(2) Error: Cannot find module ‘node-sass’

sass-loader@8.0.0以上版本的时候不需要再安装node-sass, 有时候也会报没有python,也是因为node-sass 需要这个环境导致

解决办法:

npm install --global --production windows-build-tools
npm install node-sass --save-dev

当然大多数时候是你电脑本机的node版本和项目里的 node-sass包之间的版本存在兼容问题,可以通过node-sass查看版本支持情况。 image.png

五、NPM脚本命令

1.npm项目命令

npm install (简写:npm i)来安装package.json文件中的依赖包到dependencies或devDependencies。

  • dependencies:是运行时的依赖
  • devDependencies:是开发时的依赖

查看当前项目的所有 npm 脚本命令(其实也可以在package.json的看scripts对象里有什么属性)

npm run

安装包命令

npm install xxx -save-dev 
//(简写为:npm install xxx -D )自动把模块和版本号添加到devdependencies
 
npm install xxx -save 
//(简写为:npm install xxx -S)自动把模块和版本号添加到dependencies
//npm5.0.0以后,npm install = npm install --save
 
npm install xxx -g
//-g的意思是将模块安装到全局,具体安装到磁盘哪个位置,要看 npm config prefix 的位置
//例如安装指定版本的某个包:npm i vue@2.5.15

获取包安装信息

npm view xxx 
//简写npm v xxx

升级依赖包

npm update xxx -g
//全局安装更新
 
npm update xxx --save
//生产环境依赖包更新
 
npm update xxx --save-dev
//开发环境依赖包更新

卸载依赖包

npm uninstall xxx
//删除指定的xxx依赖包,并不会从package.json中删除包
 
npm uninstall xxx -g
//删除指定的全局xxx依赖包
 
npm uninstall xxx --save
npm uninstall xxx -S
//会删除package.json中 dependencies依赖的包
 
npm uninstall xxx --save-dev
npm uninstall xxx -D
//会删除package.json中devDependencies依赖的包
 
npm uninstall xxx --no-save
//不会从package.json中删除

npm uninstall的别名:npm remove、npm remove、npm rm、 npm r、npm un、 npm unlink

2.通配符&转义

*表示任意文件名,**表示任意一层子目录

"lint": "jshint *.js"
"lint": "jshint **/*.js"

将通配符传入原始命令,防止被 Shell 转义,要将星号转义

"test": "tap test/*.js"

3.传参

向 npm 脚本传入参数,要使用--标明

# package.json
"deploy": "gulp deploy",
# 命令行
$ npm run deploy -- --test

4.一个命令执行多个任务

#并行执行(即同时的平行执行),使用&符号
$ npm run serve & npm run dev
#继发执行(即只有前一个任务成功,才执行下一个任务),使用&&符号
$ npm run build && npm run deploy

yarn

一、安装理念以及破解依赖管理困境

yarn 作为区别于 npm 的依赖管理工具,诞生之初就是为了解决历史上 npm 的某些不足,比如 npm 缺乏对于依赖的完整性和一致性保障,以及 npm 安装速度过慢的问题等,尽管 npm 发展至今,已经在很多方面向 yarn 看齐,但 yarn 的安装理念仍然需要我们关注。 yarn 提出的安装理念很好的解决了当时 npm 的依赖管理问题:

  1. 确定性。通过 yarn.lock 等机制,保证了确定性,这里的确定性包括但不限于明确的依赖版本、明确的依赖安装结构等。即在任何机器和环境下,都可以以相同的方式被安装。
  2. 模块扁平化安装。将依赖包的不同版本,按照一定策略,归结为单个版本,以避免创建多个副本造成冗余。(npm 也有相同的优化)
  3. 更好的网络性能。Yarn 采用了请求排队的理念,类似并发连接池,能够更好地利用网络资源;同时引入了更好的安装失败时的重试机制。(npm 较早的版本是顺序下载,当第一个包完全下载完成后,才会将下载控制权交给下一个包)
  4. 引入缓存机制,实现离线策略。(npm 也有类似的优化)

yarn.lock 文件结构

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


"@babel/cli@^7.0.0":
  version "7.16.8"
  resolved "https://registry.npmmirror.com/@babel/cli/download/@babel/cli-7.16.8.tgz#44b9be7706762bfa3bff8adbf746da336eb0ab7c"
  integrity sha512-FTKBbxyk5TclXOGmwYyqelqP5IF6hMxaeJskd85jbR5jBfYlwqgwAbJwnixi1ZBbTqKfFuAA95mdmUFeSRwyJA==
  dependencies:
    commander "^4.0.1"
    convert-source-map "^1.1.0"
    fs-readdir-recursive "^1.1.0"
    glob "^7.0.0"
    make-dir "^2.1.0"
    slash "^2.0.0"
    source-map "^0.5.0"
  optionalDependencies:
    "@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3"
    chokidar "^3.4.0"

"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.16.7":
  version "7.16.7"
  resolved "https://registry.npmmirror.com/@babel/code-frame/download/@babel/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789"
  integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==
  dependencies:
    "@babel/highlight" "^7.16.7"

"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.4", "@babel/compat-data@^7.16.8":
  version "7.16.8"
  resolved "https://registry.npmmirror.com/@babel/compat-data/download/@babel/compat-data-7.16.8.tgz#31560f9f29fdf1868de8cb55049538a1b9732a60"
  integrity sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==

"@babel/core@^7.0.0":
  version "7.16.7"
  resolved "https://registry.npmmirror.com/@babel/core/download/@babel/core-7.16.7.tgz#db990f931f6d40cb9b87a0dc7d2adc749f1dcbcf"
  integrity sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==
  dependencies:
    "@babel/code-frame" "^7.16.7"
    "@babel/generator" "^7.16.7"
    "@babel/helper-compilation-targets" "^7.16.7"
    "@babel/helper-module-transforms" "^7.16.7"
    "@babel/helpers" "^7.16.7"
    "@babel/parser" "^7.16.7"
    "@babel/template" "^7.16.7"
    "@babel/traverse" "^7.16.7"
    "@babel/types" "^7.16.7"
    convert-source-map "^1.7.0"
    debug "^4.1.0"
    gensync "^1.0.0-beta.2"
    json5 "^2.1.2"
    semver "^6.3.0"
    source-map "^0.5.0"

"@babel/generator@^7.16.7", "@babel/generator@^7.16.8":
  version "7.16.8"
  resolved "https://registry.npmmirror.com/@babel/generator/download/@babel/generator-7.16.8.tgz#359d44d966b8cd059d543250ce79596f792f2ebe"
  integrity sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==
  dependencies:
    "@babel/types" "^7.16.8"
    jsesc "^2.5.1"
    source-map "^0.5.0"

从上面依赖版本描述的信息中,可以确定以下几点:

  1. 所有依赖,不管是项目声明的依赖,还是依赖的依赖,都是扁平化管理。
  2. 依赖的版本是由所有依赖的版本声明范围确定的,具备相同版本声明范围的依赖归结为一类,确定一个该范围下的依赖版本。如果同一个依赖多个版本共存,那么会并列归类。
  3. 每个依赖确定的版本中,是由以下几项构成:
  4. 多个依赖的声明版本,且符合 semver 规范;
  5. 确定的版本号 version 字段;
  6. 版本的完整性验证字段
  7. 依赖列表
  8. 相比 npm,Yarn 一个显著区别是 yarn.lock 中子依赖的版本号不是固定版本。  也就是说单独一个 yarn.lock 确定不了 node_modules 目录结构,还需要和 package.json 文件进行配合。

yarn install

以下是在 yarn 安装依赖时的步骤:

image.png 1、检查(checking)  主要是检查项目中是否存在一些 npm 相关的配置文件,如 package-lock.json 等。如果存在,可能会警告提示,因为它们可能会存在冲突。在这一阶段,也会检查系统 OS、CPU 等信息。

2、解析包(resolving packages)  这一步主要是解析依赖树,确定版本信息等。 首先获取项目 package.json 中声明的首层依赖,包括 dependencies, devDependencies, optionalDependencies 声明的依赖。 接着采用遍历首层依赖的方式获取依赖包的版本信息,以及递归查找每个依赖下嵌套依赖的版本信息,并将解析过和正在解析的包用一个 Set 数据结构来存储,这样就能保证同一个版本范围内的包不会被重复解析。

  • 对于没有解析过的包,首次尝试从 yarn.lock 中获取到版本信息,并标记为已解析;
  • 如果在 yarn.lock 中没有找到包,则向 Registry 发起请求获取满足版本范围的已知最高版本的包信息,获取后将当前包标记为已解析。 总之,在经过复杂的解析算法后,我们就确定了所有依赖的具体版本信息以及下载地址。

3、获取包(fetching packages)  这一步主要是利用系统缓存,到缓存中找到具体的包资源。首先会尝试在缓存中查找依赖包,如果没有命中缓存,则将依赖包下载到缓存中。 对于没有命中缓存的包,Yarn 会维护一个 fetch 队列,按照规则进行网络请求。这里也是 yarn 诞生之初解决 npm v3 安装缓慢问题的优化点,支持并行下载。

4、链接包(linking dependencies)  这一步主要是将缓存中的依赖,复制到项目目录下,同时遵循扁平化原则。前面说到,npm 优先将依赖安装到项目目录,因此需要将全局缓存中的依赖复制到项目。 在复制依赖前,Yarn 会先解析 peerDependencies,如果找不到符合 peerDependencies 声明的依赖版本,则进行 warning 提示(这并不会影响命令执行),并最终拷贝依赖到项目中。

5、构建包(building fresh package)  如果依赖包中存在二进制包需要进行编译,会在这一步进行。

npm vs. yarn

这里简单对比 npm v6 和 yarn v1. 这是我们生产开发常用的版本。 npm 和 yarn 作为两款相似的包管理工具,在一些功能实现上它们互相获取灵感。

相同点:

  1. package.json 作为项目依赖描述文件
  2. node_modules 作为依赖存储目录,yarn v2 不再是这样
  3. lockfile 锁定版本依赖,在 yarn 中叫 yarn.lock,在 npm 中叫 package-lock.json,在 npm v7 也支持了 yarn.lock。它确保在不同机器或不同环境中,能够得到稳定的 node_modules 目录结构。

差异:

  1. 依赖管理策略。
  2. lockfile。package-lock.json 自带版本锁定+依赖结构,你想改动一些依赖,可能影响的范围要比表面看起开的复杂的多;而 yarn.lock 自带版本锁定,并没有确定的依赖结构,使用 yarn 管理项目依赖,需要 package.json + yarn.lock 共同确定依赖的结构。。
  3. 性能。(对比 npm v6 和 yarn v1)目前 npm v7 优化了缓存和下载网络策略,性能的差异在缩小

yarn 安装依赖时手动选择版本

yarn add 依赖名@版本号

版本号尽可能高一些,必须比现有的所有版本都要高(这步是关键)

yarn add html-webpack-plugin@999 -D

选择合适的版本 image.png

版本说明:

  • alpha版:内部测试版。
  • beta版:公开测试版。
  • rc版:候选版本(可用于学习,不是特别重要的项目可使用此版本)
  • stable版:稳定版(开发使用)

pnpm

一、基础概念

定义

快速的,节省磁盘空间的包管理工具。作用是跟npmyarn一样的呢。那既然功能是一样的为什么又要出现一个pnpm呢?通过官方的文档我们可以看出来它的优势在于:

  • 包安装速度极快
  • 磁盘空间利用效率高

特点

(1)包安装速度极快

比传统方案安装包的速度快了两倍,以下是官方给出的benchmarks(对比了npm, pnpm, Yarn Classic, and Yarn PnP),在多种常见情况下,执行install的速度比较。

image.png

为什么pnpm要比其他的包处理器要快呢?

主要得益于它的包管理机制,实现了节约磁盘空间并提升安装速度(也就是第二个优势)

(2)磁盘空间利用效率高

pnpm 的 node_modules 布局使用符号链接来创建依赖项的嵌套结构。node_modules 中每个包的每个文件都是来自内容可寻址存储的硬链接。

那么为什么说pnpm这种基于内容寻址的方式对磁盘空间利用效率比较高呢

  • 不会重复安装同一个包。使用npm/yarn 的时候,如果100个包依赖express ,那么就可能安装了100次express ,磁盘中就有100个地方写入了这部分代码。但是pnpm会只在一个地方写入这部分代码,后面使用会直接使用硬链接。
  • 即使一个包的不同版本,pnpm 也会极大程度地复用之前版本的代码。举个例子,比如 express4.18.1 和epxress5.0.0-beta.1的两个版本升级对比,epxress5.0.0-beta.1只是重新下载了19个新的更新的包。

二、pnpm与npm & yarn的区别

pnpmnpmyarn的不同点主要是从包的依赖管理方面来说明。

npm2

npm2生成的依赖管理比较的简单直接,会按照安装包的依赖树形结构直接填充在本地的目录结构下:

比如expresskoa他们会同时依赖accepts,那么在install之后生成的node_modules就会是如下结构:

image.png

npm2的这种方式的优点就是比较的直观,但是呢缺点也是显而易见的就是:

  1. 层级依赖过深
  2. 相同包的相同版本会多次被下载,利用率低,占用磁盘空间大

npm3/yarn

针对npm2的两个缺点呢,npm3做了个改变,不再使用嵌套的结构了,而是讲依赖进行打平,这样就能解决层级依赖深和包的利用率的问题,那么上面的依赖关系就会变成下面这个样子:

image.png

我们可以看出,express@4.18.1koa@2.13.4引用了accepts的版本是一样的,这样才会被平铺在node_modules下,那如果引用的包的版本不一样又是什么样的情况呢?就像debug,http-errorsstatuses等这几个包

  • express@4.18.1引入的是:debug@2.6.9http-errors@2.0.0statuses@2.0.1
  • koa@2.13.4引入的是:debug@4.3.2http-errors@1.6.3statuses@1.5.0

此时,在node_modules结构是下面这个样子:

image.png

至于为什么说express@4.18.1的依赖包会在根目录的node_modules下呢,这是npm自己的一个规则,具体可以看一下与你项目相关的npm知识总结

npm3的这种平铺方式确实是解决了层级依赖深和包的利用率的问题,但是也引入了其他的问题:

  1. 对没有手动引入的包,例如express@4.18.1依赖的cookie,项目中手动引入,但是依然可以使用,这样就造成了如果哪天express改变了策略不在使用cookie,而我们的项目中又使用了cookie,这样就会导致项目无法启动

  2. 虽然npm是共享了相同版本的依赖,但是如果版本不同,npm还是会完整的下载两个不同的版本,这样也会有依赖的冗余

pnpm

pnpm为了解决npm3带来的一些问题呢,采用了另外一种方式来管理依赖:pnpm 的 node_modules 布局使用符号链接来创建依赖项的嵌套结构管。

node_modules 中.pnpm下每个包的每个文件都是来自内容可寻址存储的硬链接。

这是 node_modules 中的唯一的“真实”文件。 一旦所有包都硬链接到 node_modules,就会创建符号链接来构建嵌套的依赖关系图结构。

我们用pnpm安装一下express@4.18.1koa@2.13.4,生成的目录如下所示:

image.pngnode_modules中的依赖只有在package.json中手动引入的依赖expresskoa,细心的你会发现这两个文件后面有一个箭头,而这个箭头就是pnpm使用的软连接的方式指向了.pnpm文件真正的文件:

image.png

这些文件会在内存中统一存储,如果有依赖不同版本的时候,pnpm也只会下载不同版本中不同的内容。

官方对比

image.png

三、安装和使用

安装

(1)使用独立脚本安装

curl -fsSL https://get.pnpm.io/install.sh | sh -
或者
wget -qO- https://get.pnpm.io/install.sh | sh -

(2)使用npm安装

npm install -g pnpm

(3)使用homebrew安装

brew install pnpm

使用

以下是列出常用的命令,具体可以参考官网管理依赖

(1)安装依赖包

pnpm add <pkg>

CommandMeaning
pnpm add xxx保存到dependencies
pnpm add -D xxx保存到devDependencies
pnpm add -O xxx保存到optionalDependencies
pnpm add -g xxx全局安装
pnpm add xxx@nextnext标签下安装
pnpm add xxx@3.0.0安装指定版本3.0.0

(2)下载所有依赖

pnpm install or pnpm i

(3)指定的范围更新软件包的最新版本

pnpm update or pnpm up

CommandMeaning
pnpm up遵循package.json指定的范围更新所有的依赖项
pnpm up --latest更新所有依赖项,此操作会忽略package.json指定的范围
pnpm up xxx@2xxx更新到v2上的版本
pnpm up xxx/*更新xxx范围内的所有依赖项

(4)删除依赖

pnpm removepnpm rmpnpm uninstallpnpm un

(5)运行脚本

  • 运行一个在 package的 manifest 文件中定义的脚本:pnpm run
  • 运行在 package 的 scripts 对象中test 属性指定的任意的命令:pnpm test
  • 从 create-* 或 @foo/create-* 启动套件创建项目:pnpm create,例如pnpm create react-app my-app
  • 运行在 package 的 scripts 对象中start 属性指定的任意的命令:pnpm start orrun start