包管理是使用 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
选项太多!这里有超过 3000 个与关键字 linter 匹配的包,您将如何决定要使用哪一个?
首先,您可以缩小一下搜索范围。从第 3 单元 得知,JavaScript 是一个 ECMAScript 实现,因此让我们来看看在向搜索词中添加 ecmascript 后会发生什么。
图 2. 在 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
在该示例中,假设您选择在质量、流行度和维护方面平均值最高的包,最终就得到了 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-test
和 npm 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.json
内 dependencies
对象中列出的所有模块。(您将在第 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 prune
和 npm 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),运行 node
和 npm
的版本检查以及 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
子命令):