Node.js 包管理

Xebcnor     最近更新时间:2019-10-10 11:22:23

667

包管理是使用 Node.js 进行编程的一个组成部分。Node Package Manager(npm)不仅仅是一个程序,还是一个完整的生态系统。在 Node.js 学习路径中,我们来认识 npm 及其所有组件:

  • 软件包的 npm 注册表。

  • 用于管理这些包的 npm 命令行界面(CLI)程序。

  • npm 网站,可用于获取帮助并参与其中。

快速说明:在谈论 npm 生态系统时,我会将其称为 npm。在专门提及 npm CLI 时,我将使用 npm

获取代码

我的 GitHub 存储库中提供了跟随此学习路径中的示例进行操作所需的代码。

npm 注册表

在第 3 单元中,您了解到 npm 注册表包含数十万个开源 Node 包,这些包由大型互动开发者社区提供支持。

在第 4 单元和第 5 单元中,您可能会从 npm 注册表经常出现的情况推断出,该注册表作为 特定 位置,用于寻找要在 Node 应用程序中使用的模块。

在知道包名的情况下找到并安装该包,这是一种情况,而在另一种情况下,假设您正在寻找 一种软件,而不是特定的包。您将如何在注册表中找到所需内容?

例如,假设您在查找 JavaScript linter。要在 npm 注册表中众多的 linter 中查找某一项,转到 npm 网站。在页面顶部的搜索字段中输入 linter,您将看到以下结果:

图 1. 在 npmjs.com 中搜索   linter

在 npmjs.com 中搜索 linter

选项太多!这里有超过 3000 个与关键字 linter 匹配的包,您将如何决定要使用哪一个?

首先,您可以缩小一下搜索范围。从第 3   单元 得知,JavaScript 是一个 ECMAScript 实现,因此让我们来看看在向搜索词中添加 ecmascript 后会发生什么。

图 2. 在 npmjs.com 中搜索   ecmascript linter

在 npmjs.com           中搜索 ecmascript linter

这样显然更好。筛选 50 个包比 3000 个包更容易。但是,进一步缩小搜索范围会不会有所帮助?

包评分

npmjs.com 的搜索功能(由 npms.io 提供)提供了三个有用的关键指标:

  • 流行度

  • 质量

  • 维护

npm 注册表中的每个包都受制于计算这些分数的算法。这些指标大体上意味着您对它们的认识,但如果想要获取更详细的解释,可查看 npms.io 上的About 页面。

分数以条形图格式显示在每个条目的右上角,p 表示流行度,q 表示质量,m 表示维护。将鼠标悬停在每个链接上,就会弹出相关的数字分数。(我正在使用 Chrome;您看到的情况可能会有所不同。)

您也可以直接在 npms.io 上搜索包,同样搜索 ecmascript linter 产生了 53 条结果:

图 3. 在 npms.io 中搜索 ecmascript linter

在 npms.io 中搜索           ecmascript linter

在该示例中,假设您选择在质量、流行度和维护方面平均值最高的包,最终就得到了 eslint,分数分别为 97、85 和 100,平均分为 94。

npm CLI

在选择包后,就需要进行安装,这会将我们带到 npm 命令行界面(CLI)。

该 CLI 是一个程序,充当 Node 程序的包管理器。Node 随附该 CLI。

您所知道的 npm

您已经在本课程中多次使用了 npm。在第 4 单元中,您使用 npm 安装了类似 nodemon 这样的包:

1
npm install -g nodemon

您还使用 npm 运行了 server.js(通过 npm start),运行了功能测试(通过 npm test),甚至还从 package.json 运行了自定义脚本(通过 npm run load-testnpm run start-dev)。

现在,我们将更仔细地看一下 npm 的功能及其用途。

使用 npm 安装本地包

在第 6 单元中,您使用 npm 安装了 sqlite3 模块,该模块在购物清单应用程序的 package.json 文件中被指定为依赖项:

清单 1.   /Node.js/Course/Unit-6/package.json
1
2
3
4
5
6
7
8
9
10
11
12
{
  "name": "shopping-list",
  "version": "1.0.0",
  "description": "Shopping List",
 .
 .
 .
  "homepage": "https://github.com/jstevenperry/IBM-Code#readme",
  "dependencies": {
    "sqlite3": "^4.0.1"
  }
}

npm 没有参数时,它会安装 package.jsondependencies 对象中列出的所有模块。(您将在第 8 单元中了解有关 package.json 的更多信息。)

安装一组新包

我们将 npm install 与不同于第 6 单元中所用的 package.json 一起使用。这将安装一组新包和依赖项。

首先,导航到我的 GitHub   存储库中的 Node.js/Course/Unit-7 目录,在此会看到以下 package.json。须注意,该文件包含用于运行 eslint JavaScript linter 的 pretest 脚本。

清单 2. 带有 eslint 依赖项的   package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
  "name": "Unit-7",
  "version": "1.0.0",
  "description": "Sample code for Unit 7",
  "main": "logger.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "pretest": "eslint --ignore-path ../../.gitignore ."
  },
  "keywords": [],
  "author": "J Steven Perry",
  "license": "Apache-2.0",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/jstevenperry/IBM-Code"
  },
  "devDependencies": {
    "babel-eslint": "^8.2.5",
    "eslint": "^5.1.0",
    "eslint-config-strongloop": "^2.1.0"
  }
}

如果运行 npm install,输出将类似如下:

1
2
3
4
5
$ npm install
added 164 packages from 190 contributors and audited 333 packages in 3.368s
found 0 vulnerabilities
 
$

获取有关已安装软件的信息

要查看所安装的 eslint 的版本,可运行 npm list eslint 命令

1
2
3
4
5
$ npm list eslint
Unit-7@1.0.0 /Users/sperry/home/development/projects/IBM-Code/Node.js/Course/Unit-7
└── eslint@5.1.0
 
$

要查看 node_modules 中的 完整依赖项树,可在不指定任何模块的情况下运行 npm list。输出看起来类似如下(由于非常冗长,此处已有所省略):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ npm list
Unit-7@1.0.0 /Users/sperry/home/development/projects/IBM-Code/Node.js/Course/Unit-7
├─┬ babel-eslint@8.2.5
│ ├─┬ @babel/code-frame@7.0.0-beta.44
│ │ └─┬ @babel/highlight@7.0.0-beta.44
│ │   ├── chalk@2.4.1 deduped
│ │   ├── esutils@2.0.2 deduped
│ │   └── js-tokens@3.0.2 deduped
.
.
│ │ └── estraverse@4.2.0
│ └── eslint-visitor-keys@1.0.0
├─┬ eslint@5.1.0
│ ├─┬ ajv@6.5.2
│ │ ├── fast-deep-equal@2.0.1
│ │ ├── fast-json-stable-stringify@2.0.0
│ │ ├── json-schema-traverse@0.4.1
│ │ └─┬ uri-js@4.2.2
│ │   └── punycode@2.1.1
│ ├─┬ babel-code-frame@6.26.0
.
.
│ │ ├── lodash@4.17.10 deduped
│ │ ├─┬ slice-ansi@1.0.0
│ │ │ └── is-fullwidth-code-point@2.0.0 deduped
│ │ └── string-width@2.1.1 deduped
│ └── text-table@0.2.0
└── eslint-config-strongloop@2.1.0
$

在安装 eslint 后,您可以使用针对 logger.js 的 strongloop 编码样式查明所生成的错误消息类型。

要运行 eslint,可运行上述清单 2 中的 pretest 脚本,这次使用 npm run pretest 命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ npm run pretest
 
> Unit-7@1.0.0 pretest /Users/sperry/home/development/projects/IBM-Code/Node.js/Course/Unit-7
> eslint --ignore-path ../../.gitignore .
 
 
/Users/sperry/home/development/projects/IBM-Code/Node.js/Course/Unit-7/logger.js
  91:10  error  'setMessageOnlyOutput' is defined but never used  no-unused-vars
  92:3   error  'messageOnlyOutput' is not defined                no-undef
 
? 2 problems (2 errors, 0 warnings)
 
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! Unit-7@1.0.0 pretest: `eslint --ignore-path ../../.gitignore .`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the Unit-7@1.0.0 pretest script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
 
npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/sperry/.npm/_logs/2018-07-11T21_13_15_041Z-debug.log
$

第一次运行时,linter 发现多个问题的情况并不少见。我实际上清理了 logger.js 来捕获上面的输出。

此外,您还可以配置 strongloop   config 和 eslint 规则。您可以在 Unit-7 源代码的 .eslintrc.json 文件中看到这一点,我在此覆盖了 max-len 规则(80 个字符对我来说似乎很保守)。

使用 npm 安装全局包

就像本地包一样,您已经有了一些关于全局包的实践。在第 4 单元中,您安装了全局包 nodemon。正如我在那里提到的,全局安装会将包添加到 PATH ,因此您可以从计算机上的任何位置运行该包,这对于像 nodemon 这样的 CLI 包特别有用。

用于安装全局包的命令是 npm install -g PACKAGE_NAME,其中 PACKAGE_NAME 是包的名称,如 nodemon

本地安装与全局安装

npm 的开发者对于何时全局安装包以及何时不全局安装包有一个经验法:

  • 如果包是您需要从计算机上的任何位置运行的命令行工具,那么全局安装该包。

  • 如果包是应用程序依赖项,那么本地安装该包。

npm 实用程序

npm 不仅仅可用于实现简单安装。 我最喜欢的两个用于 npm CLI 的实用程序是 npm prunenpm doctor

npm prune

如果您像我一样,在开发过程中进行实验,在落实最终解决方案之前尝试多个选项,那么不幸的是,这可能会导致无关的依赖项,这些是您不会使用的安装包。

例如,假设您要尝试 fluid-eslint,因而安装此项:

1
2
3
4
5
6
7
8
$ npm install fluid-eslintnpm 注册表中的每个包都受制
+ fluid-eslint@2.10.2
added 72 packages from 91 contributors and audited 795 packages in 4.877s
found 0 vulnerabilities
$ npm list fluid-eslint
Unit-7@1.0.0 /Users/sperry/home/development/projects/IBM-Code/Node.js/Course/Unit-7
└── fluid-eslint@2.10.2
$n

在某一时刻,您决定不再使用 fluid-eslint。将其从 package.json 中删除,但从 package.json 中删除包并不会更改 node_modules 的内容。fluid-eslint 及其依赖项仍位于 node_modules 中。

未在 package.json 中列为依赖项并且不是另一个包依赖项的已安装顶级包会被视为无关的包。您需要一种可删除无关依赖项的方法,此时 npm prune 即可派上用场。

删除无关依赖项

要识别无关包,可运行 npm list。所有无关的包都将列在依赖项树的底部,并附带一条错误消息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ npm list
Unit-7@1.0.0 /Users/sperry/home/development/projects/IBM-Code/Node.js/Course/Unit-7
├─┬ babel-eslint@8.2.5
│ ├─┬ @babel/code-frame@7.0.0-beta.44
│ │ └─┬ @babel/highlight@7.0.0-beta.44
│ │   ├── chalk@2.4.1 deduped
│ │   ├── esutils@2.0.2 deduped
│ │   └── js-tokens@3.0.2 deduped
│ ├─┬ @babel/traverse@7.0.0-beta.44
.
.
│ │ ├─┬ slice-ansi@1.0.0
│ │ │ └── is-fullwidth-code-point@2.0.0 deduped
│ │ └── string-width@2.1.1 deduped
│ └── text-table@0.2.0
├── eslint-config-strongloop@2.1.0
└── fluid-eslint@2.10.2 extraneous
 
npm ERR! extraneous: fluid-eslint@2.10.2 /Users/sperry/home/development/projects/IBM-Code/Node.js/Course/Unit-7/node_modules/fluid-eslint
$

须注意,fluid-eslint 被列为 extraneous

现在,您可以使用 npm prune CLI 实用程序从 node_modules 中删除 fluid-eslint 及其依赖项:

1
2
3
4
5
$ npm prune
removed 72 packages and audited 333 packages in 1.59s
found 0 vulnerabilities
 
$

如果您使用 npm list fluid-eslint 运行 npm install,那么将会看到所有痕迹都已消失:

1
2
3
4
5
6
7
8
$ npm install
audited 333 packages in 1.434s
found 0 vulnerabilities
 
Ix:~/home/development/projects/IBM-Code/Node.js/Course/Unit-7 sperry$ npm list fluid-eslint
Unit-7@1.0.0 /Users/sperry/home/development/projects/IBM-Code/Node.js/Course/Unit-7
└── (empty)
$

npm doctor

npm doctor 是一个方便的实用工具,可确保您的 npm 环境正常运行。其输出如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
$ npm doctor
Check                               Value                        Recommendation
npm ping                            OK
npm -v                              v6.1.0
node -v                             v10.6.0
npm config get registry             https://registry.npmjs.org/
which git                           /usr/local/bin/git
Perms check on cached files         ok
Perms check on global node_modules  ok
Perms check on local node_modules   ok
Verify cache contents               verified 5660 tarballs
$

它会运行许多检查,包括通过 npm ping 确保您的计算机可以连接到 npm 注册表(位于 registry.npmjs.org),运行 nodenpm 的版本检查以及 git 程序的路径等等。

有关 npm doctor 的更多信息,可查看其文档页面。

有关 npm 的更多信息

npm 的完整指南超出了本课程的范围,尤其是因为这些信息已经存在于 npm   文档中。如果您要了解更多信息,那么务必查看 npm 文档。在本课程的其余部分中,您将会使用 npm,所以我鼓励您熟悉这方面的内容。

npm 的备选项

虽然 npm 是最早的 Node.js 包管理器(可以追溯到 2010 年 1 月发行的 V0.0.1),但它不是唯一的包管理器。在本部分中,我将展示一些您应该了解的内容,先从以下可能最受欢迎的 npm 替换项开始:Yarn。

Yarn

Yarn 是用于 Node 的包管理器,与 npm CLI 一样使用一个 npm 注册表。它由 Facebook   开发,并于 2016 年 10 月发布。

Yarn 在 npm 上提供了许多改进,包括:

Yarn 使用一个 npm 注册表?

我认为 Yarn 使用一个 npm 注册表,是因为在撰写本文时,registry.yarnpkg.com 实际上是 registry.npmjs.org   的反向代理,这多半出于性能原因。虽然存在一个建议弃用 registry.yarnpkg.com 的  yarnpkg GitHub 问题,但使用中的注册表对大多数   Node 开发者而言都应该是不可见的。

  • 脱机访问:Yarn 允许您访问已下载的包,而 npm CLI 需要 Internet 访问权限才能运行 npm install(除非您使用 npm pack   或类似 offline-npm 的附加组件)。

  • 许可检查:Yarn 为依赖项提供内置许可检查,而 npm 需要类似 license-checker 的附加组件。

安装 Yarn

您可以使用以下命令从 npm 注册表将 Yarn 安装为全局包:npm install -g yarn。Yarn 就像 npm 一样使用 package.json 。要安装包,只需使用 yarn install 而不是 npm install

1
2
3
cd IBM-Code/Node.js/Course/Unit-7
rm -Rf node_modules
yarn install

您应该会看到类似如下的输出:

1
2
3
4
5
6
7
8
9
10
Ix:~/src/projects sperry$ cd IBM-Code/Node.js/Course/Unit-7/
Ix:~/src/projects/IBM-Code/Node.js/Course/Unit-7 sperry$ rm -Rf node_modules/
Ix:~/src/projects/IBM-Code/Node.js/Course/Unit-7 sperry$ yarn install
yarn install v1.7.0
[1/4]   Resolving packages...
[2/4]   Fetching packages...
[3/4]   Linking dependencies...
[4/4]   Building fresh packages...
?  Done in 2.32s.
Ix:~/src/projects/IBM-Code/Node.js/Course/Unit-7 sperry$

然后,您可以运行 yarn pretest,就像运行 npm run pretest 一样( Yarn 会判定 pretest 是一个脚本,您因而可跳过 run 子命令):

展开阅读全文