如何利用 npm 和 package.json 使用 Node.js 模块

作者选择了 开放式互联网 / 自由言论基金作为 写给捐赠计划的一部分接受捐款。

介绍

由于其快速的输入/输出(I/O)性能和其在众所周知的JavaScript语言的基础等功能,Node.js(https://nodejs.org/en/)迅速成为后端Web开发的流行的运行时环境,但随着兴趣的增加,更大的应用程序被构建,管理代码库的复杂性和依赖性变得更加困难。

Node.js 包管理器(npm)是 Node.js 生态系统中的默认和最流行的包管理器,主要用于在 Node.js 项目中安装和管理外部模块。

  • 项目所需的所有模块及其安装版本
  • 项目的所有元数据,如作者、许可证等
  • 可运行以自动化项目中的任务的脚本

当您创建更复杂的 Node.js 项目时,使用 package.json 文件管理您的元数据和依赖性将为您提供更可预测的构建,因为所有外部依赖性都保持相同。

在本教程中,您将使用 npm 管理软件包. 第一步是创建和理解 package.json 文件. 然后您将使用它来跟踪您在项目中安装的所有模块. 最后,您将列出您的软件包依赖性,更新您的软件包,卸载您的软件包,并进行审计,以便在您的软件包中找到安全漏洞。

前提条件

要完成本教程,您将需要:

步骤 1 — 创建一个 package.json 文件

我们通过设置示例项目开始本教程 - 一个虚构的Node.js定位器模块,该模块获得用户的IP地址并返回原产国。

首先,您将创建一个package.json文件来存储有关项目的有用的元数据,并帮助您管理项目的依赖 Node.js 模块。正如后缀所示,这是一个 JSON (JavaScript Object Notation) 文件。 JSON 是一个用于共享的标准格式,基于 JavaScript 对象并由作为关键值对存储的数据组成。

由于一个 package.json 文件包含许多属性,所以手动创建,而无需从其他地方复制和粘贴模板,可能很麻烦。 为了使事情变得更容易,npm 提供了 init 命令。

使用init命令

首先,设置一个项目,这样你就可以练习管理模块. 在你的壳中,创建一个名为locator的新文件夹:

1mkdir locator

然后进入新文件夹:

1cd locator

现在,通过输入开始互动提示:

1npm init

<$>[注意] 注意:如果您的代码将使用 Git 进行版本控制,请先创建 Git 存储库,然后运行 npm init. 该命令会自动理解它位于一个 Git 支持的文件夹中。 如果设置了 Git 远程,它会自动填写您的 package.json 文件的 存储库bughomepage 字段。 如果您在创建 package.json 文件后启动了 repo,您将需要自己添加这些信息。 有关 Git 版本控制的更多信息,请参阅我们的 介绍 Git: 安装,使用和分支系列。 <$>

您将获得以下输出:

 1[secondary_label Output]
 2This utility will walk you through creating a package.json file.
 3It only covers the most common items, and tries to guess sensible defaults.
 4
 5See `npm help init` for definitive documentation on these fields
 6and exactly what they do.
 7
 8Use `npm install <pkg>` afterwards to install a package and
 9save it as a dependency in the package.json file.
10
11Press ^C at any time to quit.
12package name: (locator)

默认情况下,该命令假定它是您所在的文件夹的名称. 每个属性的默认值都显示在中 (). 由于这个教程的默认值为 name,请按 ENTER来接受它。

要输入的下一个值是版本。除了名称之外,如果您的项目将与 npm 包存储中的其他人共享,则需要此字段。

<$>[注] 注: Node.js 包预计将遵循 Semantic Versioning (semver) 指南.因此,第一个数字将是主要版本号,只有在 API 更改时才会发生变化。

ENTER,以便接受1.0.0的默认版本。

下一个字段是描述 - 一个有用的字符串来解释你的 Node.js 模块做什么. 我们的虚构的定位器项目将获得用户的 IP 地址,并返回原产国。

如果有人安装并要求您的模块,您在入口点中设置的内容将是您加载的程序的第一个部分。 该值必须是JavaScript文件的相对位置,并将添加到package.json属性。 按ENTER以保持index.js的默认值。

<$>[注] :大多数模块的主要输入点是index.js文件,这是一个package.json主要属性的默认值,这是npm模块的输入点。

在许多受欢迎的 Node.js 模块中,测试是用 Mocha, Jest, Jasmine或其他测试框架编写和执行的。

然后,init命令将要求项目的 git 存储库,该存储库可能存在于 GitHub 等服务上(有关更多信息,请参阅 [GitHub 存储库文档])。

存储提示后,命令会要求关键字。 这个属性是一个具有有用的术语的字符串组,人们可以用来找到您的存储库。 最好有一个非常相关的字符串组,以便搜索更有针对性。 列出这些关键字作为一个分开每个值的字符串。 对于这个样本项目,请在提示中键入ip,geo,country。 完成的package.json将有三个项目在关键字的数组中。

提示中的下一个字段是作者。这对希望与您联系的模块用户有用。例如,如果有人在您的模块中发现了一种利用,他们可以用此来报告问题,以便您可以修复它。作者字段是以下格式的字符串:名称<电子邮件>(网站)。例如,Sammy<sammy@your_domain>(https://your_domain)是一个有效的作者。电子邮件和网站数据是可选的 – 一个有效的作者可能只是一个名字。添加您的联系信息作为作者并通过ENTER确认。

最后,您将被提示选择许可证。这决定了用户在使用您的模块时的法律权限和限制。 许多 Node.js 模块都是开源的,因此 npm 将默认值设置为 ISC

在此时,您将审查您的许可选项,并决定什么是最佳的项目。有关不同类型的开源许可证的更多信息,请参阅此 开放源代码倡议的许可证列表

init命令现在会显示它要创建的package.json文件,它将看起来像这样:

 1[secondary_label Output]
 2About to write to /home/sammy/locator/package.json:
 3
 4{
 5  "name": "locator",
 6  "version": "1.0.0",
 7  "description": "Finds the country of origin of the incoming request",
 8  "main": "index.js",
 9  "scripts": {
10    "test": "echo \"Error: no test specified\" && exit 1"
11  },
12  "keywords": [
13    "ip",
14    "geo",
15    "country"
16  ],
17  "author": "Sammy <sammy@your_domain> (https://your_domain)",
18  "license": "ISC"
19}
20
21Is this OK? (yes)

一旦信息匹配您在这里看到的内容,请按ENTER,完成此过程并创建package.json文件. 使用此文件,您可以保持您为项目安装的模块的记录。

现在你有你的 package.json 文件,你可以测试安装模块在下一步。

步骤2:安装模块

在软件开发中,使用外部库在项目中执行辅助任务是常见的,这使开发人员能够专注于业务逻辑,并通过使用其他人编写的工具和代码更快、更有效地创建应用程序,以完成需要的任务。

例如,如果我们的样本定位器模块需要进行外部 API 请求以获取地理数据,我们可以使用 HTTP 库来简化这个任务,因为我们的主要目标是将相关的地理数据返回用户,所以我们可以安装一个使 HTTP 请求更容易的包,而不是为我们自己重写这个代码,这是一个超出我们的项目范围的任务。

在您的定位器应用程序中,您将使用 axios库,这将帮助您进行 HTTP 请求。

1npm install axios --save

您将此命令从npm install开始,该命令将安装包(为了简化,您也可以使用npm i)然后列出您想要安装的包,分隔一个空间,在这种情况下,这是axios

安装库后,您将看到类似于以下的输出:

1[secondary_label Output]
2...
3+ [email protected]
4added 5 packages from 8 contributors and audited 5 packages in 0.764s
5found 0 vulnerabilities

现在,使用您选择的文本编辑器打开package.json文件,本教程将使用nano:

1nano package.json

您将看到一个新的财产,如下所示:

 1[label locator/package.json]
 2{
 3  "name": "locator",
 4  "version": "1.0.0",
 5  "description": "Finds the country of origin of the incoming request",
 6  "main": "index.js",
 7  "scripts": {
 8    "test": "echo \"Error: no test specified\" && exit 1"
 9  },
10  "keywords": [
11    "ip",
12    "geo",
13    "country"
14  ],
15  "author": "Sammy sammy@your_domain (https://your_domain)",
16  "license": "ISC",
17  "dependencies": {
18    "axios": "^0.27.2"
19  }
20}

保存选项告诉npmpackage.json更新到刚刚安装的模块和版本。

<$>[注] :您可能已经注意到版本编号之前的^axios依赖性。请记住,语义版本由三个数字组成: MAJOR, MINORPATCH。符号^意味着任何更高的MINOR或PATCH版本都会满足这个版本限制。

当您完成审查 package.json 时,关闭文件. 如果您使用 nano 来编辑文件,您可以通过按 CTRL + X 然后按 ENTER 来完成。

发展依赖

用于项目开发但不用于构建或在生产中运行的包称为 development dependencies. 它们不需要您的模块或应用程序在生产中工作,但在编写代码时可能有用。

例如,开发人员通常会使用 code linters来确保他们的代码遵循最佳实践,并保持风格一致,虽然这对开发有用,但这只会增加可分布的尺寸,而不会在部署时提供实实在在的好处。

安装一个灯泡作为您的项目的开发依赖。

1npm i [email protected] --save-dev

在此命令中,您使用了--save-dev旗帜,将eslint保存为仅用于开发的依赖性。 另外,请注意,您已将@8.0.0添加到您的依赖性名称中。 当模块更新时,它们将被标记为版本。 @将告诉 npm 寻找您正在安装的模块的特定标签。 没有指定标签,npm 会安装最新标记的版本。 再次打开package.json:

1nano package.json

这将显示如下:

 1[label locator/package.json]
 2{
 3  "name": "locator",
 4  "version": "1.0.0",
 5  "description": "Finds the country of origin of the incoming request",
 6  "main": "index.js",
 7  "scripts": {
 8    "test": "echo \"Error: no test specified\" && exit 1"
 9  },
10  "keywords": [
11    "ip",
12    "geo",
13    "country"
14  ],
15  "author": "Sammy sammy@your_domain (https://your_domain)",
16  "license": "ISC",
17  "dependencies": {
18    "axios": "^0.19.0"
19  },
20  "devDependencies": {
21    "eslint": "^8.0.0"
22  }
23}

「slint」已被儲存為「devDependencies」,以及您先前指定的版本號碼。

自动生成的文件:node_modules 和package-lock.json

当您首次在 Node.js 项目中安装包时,npm 会自动创建node_modules文件夹,以存储您项目所需的模块和您之前审查的package-lock.json文件。

确认这些在您的工作目录中。在您的壳中,键入ls并按ENTER

1[secondary_label Output]
2node_modules package.json package-lock.json

node_modules文件夹包含您项目所安装的每一个依赖性。在大多数情况下,您不应该 **将此文件夹编入您的版本控制的存储库。随着您安装更多的依赖性,此文件夹的大小会迅速增加。

虽然package.json文件列出了告诉我们该项目应该安装的适当版本的依赖性,但package-lock.json文件会跟踪package.jsonnode_modules中的所有变化,并告诉我们安装的包的确切版本。

从 package.json 安装

使用「package.json」和「package-lock.json」文件,您可以在开始开发新项目之前快速设置相同的项目依赖性。

1cd ..
2mkdir cloned_locator

进入你的新目录:

1cd cloned_locator

现在将package.jsonpackage-lock.json文件从locator复制到cloned_locator:

1cp ../locator/package.json ../locator/package-lock.json .

要安装该项目所需的模块,键入:

1npm i

npm 会检查一个 package-lock.json 文件来安装模块. 如果没有锁文件可用,它会从 package.json 文件中读取,以确定安装。 安装从 package-lock.json 通常更快,因为锁文件包含模块的确切版本及其依赖性,这意味着 npm 无需花费时间找出一个合适的版本来安装。

当部署到生产时,您可能想跳过开发依赖性。 请记住,开发依赖性存储在package.jsondevDependencies部分,不会影响您的应用程序的运行。

1npm i --production

在安装过程中,--production 旗帜忽略了devDependencies 部分。

在移动到下一节之前,返回locator文件夹:

1cd ../locator

全球设施

到目前为止,您已经为定位器项目安装了npm模块, npm 还允许您安装 globally 包,这意味着该包在更广泛的系统中可供用户使用,就像任何其他壳命令一样。

例如,您可能想要博客关于您目前正在开发的定位器项目。 要做到这一点,您可以使用像 Hexo这样的库创建和管理静态网站博客。

1npm i hexo-cli -g

要在全球范围内安装一个包,请将-g标志附加到命令中。

<$>[注意] 注意:如果您在试图在全球范围内安装此软件包时收到权限错误,您的系统可能需要超级用户权限来运行该命令。

通过键入测试该包是否已成功安装:

1hexo --version

你会看到类似的输出:

 1[secondary_label Output]
 2hexo-cli: 4.3.0
 3os: linux 5.15.0-35-generic Ubuntu 22.04 LTS 22.04 LTS (Jammy Jellyfish)
 4node: 18.3.0
 5v8: 10.2.154.4-node.8
 6uv: 1.43.0
 7zlib: 1.2.11
 8brotli: 1.0.9
 9ares: 1.18.1
10modules: 108
11nghttp2: 1.47.0
12napi: 8
13llhttp: 6.0.6
14openssl: 3.0.3+quic
15cldr: 41.0
16icu: 71.1
17tz: 2022a
18unicode: 14.0
19ngtcp2: 0.1.0-DEV
20nghttp3: 0.1.0-DEV

到目前为止,您已经学会了如何使用 npm 安装模块,您可以本地安装项目的包,无论是作为生产或开发依赖,您也可以安装基于现有的 package.jsonpackage-lock.json 文件的包,从而允许您使用与同行相同的依赖来开发。

现在你可以安装模块,在下一部分,你将练习管理你的依赖的技术。

步骤三:管理模块

一个完整的包管理器可以做得比安装模块多得多。 npm 有20多个与依赖管理相关的命令可用。

  • 列出您已安装的模块.
  • 更新模块到更新的版本.
  • 卸载您不再需要的模块.
  • 对您的模块进行安全审核以发现和修复安全漏洞。

虽然这些示例将在您的定位器文件夹中完成,但所有这些命令都可以通过在其末尾附加-g标志来运行,就像您在全球安装时一样。

模块列表

如果您想知道项目中安装了哪些模块,则更容易使用列表ls命令,而不是直接阅读package.json

1npm ls

你会看到这样的输出:

1[secondary_label Output]
2├── [email protected]
3└── [email protected]

--深度选项允许您指定你想要看到的依赖树的哪个层次。当它是0,你只看到你的顶层依赖。

1npm ls --all

你会看到这样的输出:

 1[secondary_label Output]
 2├─┬ [email protected]
 3│ ├── [email protected]
 4│ └─┬ [email protected]
 5│   ├── [email protected]
 6│   ├─┬ [email protected]
 7│   │ └── [email protected]
 8│   └─┬ [email protected]
 9│     └── [email protected]
10└─┬ [email protected]
11  ├─┬ @eslint/[email protected]
12  │ ├── [email protected] deduped
13  │ ├── [email protected] deduped
14  │ ├── [email protected] deduped
15  │ ├── [email protected] deduped
16  │ ├── [email protected]
17  │ ├── [email protected] deduped
18  │ ├── [email protected] deduped
19  │ ├── [email protected] deduped
20  │ └── [email protected] deduped
21
22. . .

更新模块

保持 npm 模块的更新是很好的做法,这会提高您对模块的最新安全修复的可能性。 使用过时命令检查是否可以更新任何模块:

1npm outdated

你会得到这样的输出:

1[secondary_label Output]
2Package Current Wanted Latest Location Depended by
3eslint 8.0.0 8.17.0 8.17.0 node_modules/eslint locator

此命令首先列出了已安裝的「Package」和「現行」版本。「Wanted」列表了「package.json」中的哪個版本符合您的版本要求。

位置列表示在依赖树中包的位置。过时命令具有--深度的旗帜,如ls

您似乎可以将slint更新到更新的版本,使用updateup命令如下:

1npm up eslint

命令的输出将包含已安装的版本:

1[secondary_label Output]
2
3removed 7 packages, changed 4 packages, and audited 91 packages in 1s
4
514 packages are looking for funding
6  run `npm fund` for details
7
8found 0 vulnerabilities

要查看您目前正在使用的slint版本,您可以使用npm ls作为参数使用包名称:

1npm ls eslint

输出将类似于您之前使用的npm ls命令,但只包括eslint包的版本:

1[secondary_label Output]
2└─┬ [email protected]
3  └─┬ [email protected]
4    └── [email protected] deduped

如果你想一次更新所有模块,那么你会输入:

1npm up

移除模块

npm卸载命令可以从您的项目中删除模块,这意味着该模块将不再安装在node_modules文件夹中,也不会出现在您的package.jsonpackage-lock.json文件中。

从项目中删除依赖性是软件开发生命周期中的正常活动,依赖性可能无法像所宣传的那样解决问题,或者可能无法提供令人满意的开发体验。

假设axios不提供你想做HTTP请求的开发体验。

1npm un axios

您的输出将类似于:

1[secondary_label Output]
2removed 8 packages, and audited 83 packages in 542ms
3
413 packages are looking for funding
5  run `npm fund` for details
6
7found 0 vulnerabilities

它並沒有明確說「axios」已被移除. 若要確定它已被移除,請再次列出依賴:

1npm ls

现在,我们只看到eslint已安装:

1[secondary_label Output]
2[email protected] /home/ubuntu/locator
3└── [email protected]

这表明您已成功卸载axios包。

审计模块

npm 提供一个审计命令,以突出您依赖的潜在安全风险。 若要查看正在进行的审计,请通过运行以下操作安装 请求模块的过时版本:

当您安装这个过时的请求版本时,您会注意到类似于以下的输出:

 1[secondary_label Output]
 2npm WARN deprecated [email protected]: This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).
 3npm WARN deprecated [email protected]: This module moved to @hapi/sntp. Please make sure to switch over as this distribution is no longer supported and may contain bugs and critical security issues.
 4npm WARN deprecated [email protected]: This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).
 5npm WARN deprecated [email protected]: Use uuid module instead
 6npm WARN deprecated [email protected]: this library is no longer supported
 7npm WARN deprecated [email protected]: This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).
 8npm WARN deprecated [email protected]: request has been deprecated, see https://github.com/request/request/issues/3142
 9npm WARN deprecated [email protected]: This module moved to @hapi/hawk. Please make sure to switch over as this distribution is no longer supported and may contain bugs and critical security issues.
10
11added 56 packages, and audited 139 packages in 4s
12
1313 packages are looking for funding
14  run `npm fund` for details
15
169 vulnerabilities (5 moderate, 2 high, 2 critical)
17
18To address all issues, run:
19  npm audit fix --force
20
21Run `npm audit` for details.

npm 告訴您,您在依賴區內有減少的包和漏洞。 若要了解更多細節,請用以下方式審核您的整個項目:

1npm audit

审计命令显示了突出安全漏洞的输出表:

 1[secondary_label Output]
 2# npm audit report
 3
 4bl  <1.2.3
 5Severity: moderate
 6Remote Memory Exposure in bl - https://github.com/advisories/GHSA-pp7h-53gx-mx7r
 7fix available via `npm audit fix`
 8node_modules/bl
 9  request 2.16.0 - 2.86.0
10  Depends on vulnerable versions of bl
11  Depends on vulnerable versions of hawk
12  Depends on vulnerable versions of qs
13  Depends on vulnerable versions of tunnel-agent
14  node_modules/request
15
16cryptiles  <=4.1.1
17Severity: critical
18Insufficient Entropy in cryptiles - https://github.com/advisories/GHSA-rq8g-5pc5-wrhr
19Depends on vulnerable versions of boom
20fix available via `npm audit fix`
21node_modules/cryptiles
22  hawk  <=9.0.0
23  Depends on vulnerable versions of boom
24  Depends on vulnerable versions of cryptiles
25  Depends on vulnerable versions of hoek
26  Depends on vulnerable versions of sntp
27  node_modules/hawk
28
29. . .
30
319 vulnerabilities (5 moderate, 2 high, 2 critical)
32
33To address all issues, run:
34  npm audit fix

您可以看到漏洞的路径,有时npm为您提供方法来修复它,您可以按照建议运行更新命令,或者您可以运行修复审计子命令。

1npm audit fix

你会看到类似的输出:

 1[secondary_label Output]
 2npm WARN deprecated [email protected]: this library is no longer supported
 3npm WARN deprecated [email protected]: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
 4npm WARN deprecated [email protected]: request has been deprecated, see https://github.com/request/request/issues/3142
 5
 6added 19 packages, removed 34 packages, changed 13 packages, and audited 124 packages in 3s
 7
 814 packages are looking for funding
 9  run `npm fund` for details
10
11found 0 vulnerabilities

npm 能够安全地更新两个软件包,以相同的数量减少您的漏洞,但是,您仍然在您的依赖中有三个失败的软件包。审计修复命令并不总是解决每一个问题,尽管模块的版本可能有一个安全漏洞,但如果您将其更新到具有不同的 API 的版本,则它可能会在依赖树中更高层次地破坏代码。

您可以使用 --force 参数来确保漏洞消失,如下:

1npm audit fix --force

如前所述,除非您确信它不会破坏功能,否则不建议这样做。

结论

在本教程中,您通过各种练习来展示 Node.js 模块如何组织成包,以及这些包是如何通过 npm 来管理的。在 Node.js 项目中,您使用 npm 包作为依赖,创建和维护一个 package.json 文件 - 记录您的项目的元数据,包括您安装了哪些模块。

在未来,利用现有代码使用模块将加快开发时间,因为您不必重复功能。您还将能够创建自己的npm模块,这些模块将由其他人通过npm命令进行管理。 对于下一步,通过安装和测试各种包来体验您在本教程中学到的内容。 查看生态系统提供的功能,以便更轻松解决问题。 例如,您可以尝试 TypeScript,JavaScript的超组,或用 Cordova将您的网站变成移动应用程序。 如果您想了解更多关于Node.js的信息,请参阅我们的 其他Node.js教程

Published At
Categories with 技术
comments powered by Disqus