vv13
博客Github

NPM 的一些细节与规范

依赖管理

使用 npm 管理依赖时,我们常常会将以依赖分为以下几类:

  • dependencies: 程序在正常运行情况下所需要的依赖。
  • devDependencies: 程序在开发阶段或非运行时可能需要依赖的包。
  • peerDependencies: 程序运行时需要依赖的模块,但是自身又不必安装这些依赖。

切不可将依赖随意放置在其中,这不仅会影响到安装依赖的速度,甚至会造成不可预期的运行错误,因此在安装依赖的时候需要明确区分哪些依赖是运行时所依赖的。peerDependencies 或许有些特殊,它通常用于插件开发的模式,你的插件需要使用到某些依赖,但是你又没必要去安装它,因为宿主环境在安装你的插件时肯定会安装相应依赖,在 npm 3 到 6 的版本中,若项目在安装模块时没安装相应的 peerDependencies 的正确版本就会报错,而在 npm 7 里会默认帮你安装额外依赖。

npm ci

npm install类似,npm ci主要用于安装项目依赖,它是一种更为快速、干净的方式,两者之间的区别为:

  • 工程项目中必须包含package-lock.json或npm-shrinkwrap.json。
  • npm ci一次只能安装整个工程的包,而不能进行单个包的安装。
  • 如果node_modules已经存在了,它会被自动移除掉。
  • 它不会改变package-locks或package.json,这些项在安装时是被冻结的。

npm i --production

在服务器中安装依赖时,我们可以使用此命令来忽略掉 devDependencies 模块的安装,这样可以避免安装程序运行时不需要的模块,

命令缩写

当你熟练的使用npm命令以后,缩写能帮助人们节约不少时间,在此简单罗列一下常用的快捷参数,因为这总比你使用npm help或去官方文档搜索相关命令更节省时间:

  • npm

    • i -> install
    • -D -> --save-dev,安装依赖到开发环境,即devDependencies.
    • -S,在npm5之前代表--save,现在此参数已被移除
    • -g -> --global,安装到全局包
    • npm un -> npm uninstall
    • init -y-y表示使用默认参数,避免手动回车确认

npm-ci

package.json

publishConfig

若要频繁进行npm publish时会十分便捷,比如对scope包进行推送,npm默认推送行为是私有包,若没有进行付费注册私有库会推送失败,这时在每次推送时就应该使用:npm publish --access public,一劳永逸的办法是在package.json中加入:

  "publishConfig": {
    "access": "public"
  }

还有我们经常会修改.npmrc中的registry地址,通过换源提升依赖安装速度,但是这样在publish时也会默认使用换源地址,因此我们可以将官方仓库加入此配置里:

  "publishConfig": {
    "registry": "<http://registry.npmjs.org>"
  }

types

告诉Tpyescript和编辑器在哪里寻找类型定义。

环境管理

  • npm list -g --depth 0,查看全局安装了哪些npm包
  • npm root -g,查看全局node_modules路径
  • npm bin -g,查看全局可执行npm脚本存放的位置

当有了 nvm,就可以对全局包进行清除:

sudo rm -rf /usr/local/lib/node_modules #删除全局 node_modules 目录
sudo rm /usr/local/bin/node #删除 node
cd  /usr/local/bin && ls -l | grep "../lib/node_modules/" | awk '{print $9}'| xargs rm #删除全局 node 模块注册的软链

npmrc

npm会从以下以下源中读取配置信息,按优先级排序:

  1. 命令行标记,如--foo bar,表示参数foo的值为bar;--foo bar --bar则表示参数foo的值为bar,参数bar的值为true
  2. 环境变量,所有环境变量以npm_config_开头的都会被视作配置参数,它是不区分大小写的,

优化node_modules的体积

对用一个大型前端工程来讲,node_modules 的冗余是不可避免的,若对此有洁癖的朋友,可以安装 node-prune 定期优化文件夹。

作为一个老前端,我想你的前端工程一定快把你的硬盘撑爆了吧,于是我写了一个脚本来清理当前文件夹下的所有 node_modules 目录:npx cleanup-node-modules

版本管理

node遵守semver语义化版本管理,并使用node-semver来解析版本号,简单来说,基于npm管理的版本格式为:主版本号.次版本号.修订号,版本号的递增规则如下:

  1. 主版本号:当你做了不兼容的 API 修改。
  2. 次版本号:当你做了向下兼容的功能性新增。
  3. 修订号:当你做了向下兼容的问题修正。

先行版本号与编译元数据

除了以上三种格式,修订号后还可以加上先行版本号与编辑元数据。

先行版本号可以(MAY)被标注在修订版之后,先加上一个连接号再加上一连串以句点分隔的标识符来修饰。标识符必须(MUST)由 ASCII 字母数字和连接号 [0-9A-Za-z-] 组成,且禁止(MUST NOT)留白。数字型的标识符禁止(MUST NOT)在前方补零。先行版的优先级低于相关联的标准版本。被标上先行版本号则表示这个版本并非稳定而且可能无法满足预期的兼容性需求。范例:1.0.0-alpha、1.0.0-alpha.1、1.0.0-0.3.7、1.0.0-x.7.z.92。

通常先行版本号的常用标识为:

  • alpha,通常指内部测试版,一般不会向外部发布,会存有Bug,公测试人员使用
  • beta,也是测试版本,这个版本还会加入新的功能
  • rc,正式环境候选版本,这个版本不会加入新的功能,着重与拍错

版本编译元数据可以(MAY)被标注在修订版或先行版本号之后,先加上一个加号再加上一连串以句点分隔的标识符来修饰。标识符必须(MUST)由 ASCII 字母数字和连接号 [0-9A-Za-z-] 组成,且禁止(MUST NOT)留白。当判断版本的优先层级时,版本编译元数据可(SHOULD)被忽略。因此当两个版本只有在版本编译元数据有差别时,属于相同的优先层级。范例:1.0.0-alpha+001、1.0.0+20130313144700、1.0.0-beta+exp.sha.5114f85。

版本匹配规则

  • version,必须匹配某个版本, 如:1.1.2,表示必须依赖 1.1.2 版
  • >version,必须大于某个版本,如:>1.1.2,表示必须大于 1.1.2 版
  • >=version,可大于或等于某个版本,如:>=1.1.2,表示可以等于 1.1.2,也可以大于 1.1.2 版本
  • <version,必须小于某个版本,如:<1.1.2,表示必须小于 1.1.2 版本
  • <=version,可以小于或等于某个版本,如:<=1.1.2,表示可以等于 1.1.2,也可以小于 1.1.2 版本
  • ~version,大概匹配某个版本,如果 minor 版本号指定了,那么 minor 版本号不变,而 patch 版本号任意;如果 minor 和 patch 版本号未指定,那么 minor 和 patch 版本号任意

    如:~1.1.2,表示 >= 1.1.2 < 1.2.0,可以是 1.1.2,1.1.3,1.1.4,.....,1.1.n

    如:~1.1,表示 >= 1.1.0 < 1.2.0,可以是同上

    如:~1,表示 >= 1.0.0 < 2.0.0,可以是 1.0.0,1.0.1,1.0.2,.....,1.0.n,1.1.n,1.2.n,.....,1.n.n

  • ^version,兼容某个版本,版本号中最左边的非 0 数字的右侧可以任意,如果缺少某个版本号,则这个版本号的位置可以任意

    如:^1.1.2 ,表示 >=1.1.2 <2.0.0,可以是 1.1.2,1.1.3,.....,1.1.n,1.2.n,.....,1.n.n

    如:^0.2.3 ,表示 >=0.2.3 <0.3.0,可以是 0.2.3,0.2.4,.....,0.2.n

    如:^0.0,表示 >=0.0.0 <0.1.0,可以是 0.0.0,0.0.1,.....,0.0.n

  • x 标识符,x 的位置表示任意版本,如:1.2.x,表示可以 1.2.0,1.2.1,.....,1.2.n
  • * 标识符,任意版本,"" 也表示任意版本,如:*,表示 >=0.0.0 的任意版本
  • version1 - version2,大于等于 version1,小于等于 version2

    如:1.1.2 - 1.3.1,表示包括 1.1.2 和 1.3.1 以及他们件的任意版本

  • range1 || range2,满足 range1 或者满足 range2,可以多个范围,如:<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0,表示满足这3个范围的版本都可以