前端包管理工具

包的英语单词:Package,针对编程来说,更具象的名字叫「软件包」。代表了一组特定功能的源码集合。软件包可以是一个单独的源码文件,也可以是一个包含很多文件和目录的文件夹。具体如何呈现,就要看这个软件包提供了什么样的功能。

软件包就是有人对一些具有重复性的功能问题做出了针对性的解决方案。我们在软件开发的过程中有了相应的功能需求时,除了自己写,也可以选择直接使用这些有功能针对性的软件包。这除了可以节省自己大量的精力和时间,也能让自己少踩一些坑。

在软件开发过程中使用包不是必须的,但却是一项最佳的工程化实践。

npm

npm是什么?

npm (node package manager)为你和你的团队打开了连接整个 JavaScript 天才世界的一扇大门。它是世界上最大的软件注册表,每星期大约有 30 亿次的下载量,包含超过 600000 个 包(package)(即,代码模块)。来自各大洲的开源软件开发者使用 npm 互相分享和借鉴。包的结构使您能够轻松跟踪依赖项和版本。

npm 由三个独立的部分组成:

  • 网站
  • 注册表(registry)
  • 命令行工具(CLI)

网站 是开发者查找包(package)、设置参数以及管理 npm 使用体验的主要途径。

注册表 是一个巨大的数据库,保存了每个包(package)的信息。

CLI 通过命令行或终端运行。开发者通过 CLI 与 npm 打交道。

npm安装包

有两种方式用来安装 npm 包:本地安装和全局安装。至于选择哪种方式来安装,取决于我们如何使用这个包。

  • 如果你自己的模块依赖于某个包,并通过 Node.js 的 require 加载,那么你应该选择本地安装,这种方式也是 npm install 命令的默认行为。
  • 如果你想将包作为一个命令行工具,(比如 grunt CLI),那么你应该选择全局安装。

可以使用以下命令安装一个包:

1
$ npm install <package_name>

上述命令执行之后将会在当前的目录下创建一个 node_modules 的目录(如果不存在的话),然后将下载的包保存到这个目录下。

如果需要将包安装到全局,你应该使用以下命令:

1
$ npm install -g <package_name>

package.json

项目的 package.json 文件是配置和描述如何与程序交互和运行的中心。npm CLI用它来识别你的项目并了解如何处理项目的依赖关系。package.json 文件使 npm 可以启动你的项目、运行脚本、安装依赖项、发布到 NPM 注册表以及许多其他有用的任务。

你的项目还必须包含 package.json ,然后才能从 NPM 安装软件包。

package.json 位于项目的根目录下。

包的版本号规则

npm 包的版本号遵循以下规则:

1
2
主版本号.次版本号.修补版本号
major.minor.patch
  • major:新的架构调整,不兼容老版本
  • minor:新增功能,兼容老版本
  • patch:修复bug,兼容老版本

version

仅接受指定的版本

~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

l**atest**

安装的永远是最新发布的版本

>version

必须大于某个版本

>=version

可大于或等于某个版本

<version

必须小于某个版本

<=version

可以小于或等于某个版本

x

x的位置表示任意版本 如:1.2.x,表示可以1.2.0,1.2.1,…,1.2.n

*

任意版本,””也表示任意版本

version1 - version2

大于等于version1,小于等于version2

range1 || range2

满足range1或者满足range2,可以多个范围

如:<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 ,表示满足这三个范围都可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"dependencies": {
"foo": "1.0.0 - 2.9999.9999",
"bar": ">=1.0.2 <2.1.2",
"baz": ">1.0.2 <=2.3.4",
"boo": "2.0.1",
"qux": "<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0",
"asd": "http://asdf.com/asdf.tar.gz",
"til": "~1.2",
"elf": "~1.2.3",
"two": "2.x",
"thr": "3.3.x",
"lat": "latest",
"dyl": "file:../dyl" // 本地路径
}
}

package-lock.json

当我们执行 npm install 命令时,会自动在项目的根目录创建一个 package-lock.json 文件,该文件可以理解成对结合了逻辑树(package.json里面定义的依赖关系树)和物理树(node_modules文件夹下的结构)的一个快照,里面有明确的各依赖版本号,实际安装的结构,也有逻辑树的结构。

该文件用于描述依赖树的详细情况,以保证可以重复安装完全相同的依赖项。并允许npm跳过先前安装包的重复元数据解析来优化安装过程。

我们无法直接还原项目某一时刻的所有依赖项(node_modules),但通过 package.jsonpackage-lock.json 文件,可以推导出项目某一时刻的所有依赖项并完全还原。

要完全按照 package-lock.json 文件里的定义安装依赖项,可以使用 [npm ci](https://docs.npmjs.com/cli/v9/commands/npm-ci) 命令。

yarn

yarnnpm 类似,都是包管理工具。yarn 中宣传的点是:

  • 快速:yarn会缓存下载过的每一个包,这样就不用再次下载了。同时会并行执行安装任务,以达到更快的速度。
  • 安全:yarn会校验每个已安装包的完整性,再执行它的代码。
  • 可靠:lock锁机制,保证在一个系统上运行的安装在其它任何系统上都能以完全相同的方式运行。

yarn.lock

yarn 也有与 npm 类似的lock锁机制,这些信息存放在 yarn.lock 文件下。该文件会把项目下所有依赖项以及依赖项的依赖扁平化的展示出来,对于同包不同版本且规则相容的字段会放在同一级结构中。

npm ci 命令一样使用 yarn

1
$ yarn install --frozen-lockfile

nvm

nvm (Node Version Manager),即 node 版本管理工具,可以在同一台设备上安装和切换多个 node 版本,解决 node 各种版本存在不兼容的现象。

nvm常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 安装指定 node 版本
$ nvm install <version>

# 查看本地 node 版本
$ nvm ls
# 或
$ nvm list

# 查看远程 node 版本
$ nvm ls-remote

# 使用指定 node 版本
$ nvm use <version>

# 设置默认 node 版本
$ nvm alias default <version>

# 卸载指定 node 版本
$ nvm uninstall <version>

npx

npm 从5.2版本开始,增加了 npx 命令,所以可以直接使用 npx 命令。如果不能使用,可以手动安装一下。

1
$ npm install -g npx

调用项目安装的模块

npx 想要解决的主要问题,就是调用项目内部安装的模块。比如,项目内安装了测试工具 Mocha ,一般来说,想要调用它只能在项目脚本和 package.jsonscript 字段里面,如果想在命令行下调用,必须像下面这样。

1
2
# 项目的根目录下执行
$ node-modules/.bin/mocha --version

npx 就是想解决这个问题,让项目内部安装的模块用起来更方便,只要像下面这样调用就行了。

1
$ npx mocha --version

npx 的原理很简单,就是运行的时候,会到 node_modules/.bin 路径和环境变量 $PATH 里面,检查命令是否存在。

避免全局安装模块

除了调用项目内部的模块,npx 还能避免全局安装的模块(可以指定版本号)。比如 create-react-app 这个模块就是全局安装,npx 可以运行它,而且不进行全局安装。

1
$ npx create-react-app my-react-app

上面代码运行时,npx 将 create-react-app 下载到一个临时目录(只要 npx 后面的模块无法在本地发现,就会下载同名模块),使用以后再删除,以后再使用该命令时重新下载。

--no-install 参数和--ignore-existing 参数

如果想让 npx 强制使用本地模块,不下载远程模块,可以使用 —no-install 参数,如果本地不存在该模块,就会报错。

1
$ npx --no-install http-server

反过来,如果忽略本地的同名模块,强制安装使用远程模块,可以使用 --ignore-existing 参数。比如,本地已经全局安装了 create-react-app ,但还是想使用远程模块,就用这个参数。

1
$ npx --ignore-existing create-react-app my-react-app

使用不同版本的 node

利用 npx 可以下载模块这个特点,可以指定某个版本的 Node 运行脚本。它的窍门就是使用 npm 的 node模块。

1
$ npx node@0.12.8 -v

上面命令会使用 0.12.8 版本的 Node 执行脚本。原理是从 npm 下载这个版本的 node,使用后再删掉。

某些场景下,这个方法用来切换 Node 版本,要比 nvm 那样的版本管理器方便一些。

指定参数

-p 参数参数用于指定 npx 所要安装的模块,可以在需要安装多个模块的场景时使用。

1
$ npx -p lolcatjs -p cowsay [command]

npx部分转载自 npx 使用教程 - 阮一峰的网络日志

pnpm

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

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

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

创建非扁平化的 node_modules 文件夹

node-modules-structure-8ab301ddaed3b7530858b233f5b3be57.jpg

Lockfile

您应该始终提交 Lockfilepnpm 生成的 pnpm-lock.yaml 文件)。 这是出于多种原因,主要是:

  • 在 CI 和生产环境中能够更快地完成安装,因为解析依赖的过程可以被跳过。
  • 开发,测试和生产环境之间强制执行一致的安装和解析方案,这意味着测试和生产中使用的包将与您开发项目时完全相同。
0%