包管理基础
在本文中,我们将详细了解包管理器,以便理解如何在自己的项目中使用它们——安装项目工具依赖项、保持它们更新等等。
先决条件 | 熟悉核心 HTML、CSS 和 JavaScript 语言。 |
---|---|
目标 | 了解什么是包管理器和包存储库,为什么需要它们以及如何使用它们的基本知识。 |
项目中的依赖项
**依赖项**是指可能由其他人编写且理想情况下为您解决单个问题的第三方软件。一个 Web 项目可以有任意数量的依赖项,从零到多个,并且您的依赖项可能包含您没有明确安装的子依赖项——您的依赖项可能有自己的依赖项。
项目可能需要的一个有用依赖项的简单示例是一些代码,用于计算相对日期作为人类可读的文本。您当然可以自己编写此代码,但很有可能其他人已经解决了此问题——为什么要浪费时间重新发明轮子呢?此外,可靠的第三方依赖项可能已经在许多不同的情况下进行了测试,使其比您自己的解决方案更健壮且更兼容跨浏览器。
项目依赖项可以是整个 JavaScript 库或框架(例如 React 或 Vue),也可以是非常小的实用程序(如我们的人类可读日期库),或者可以是命令行工具(如 Prettier 或 ESLint),我们在之前的文章中讨论过这些工具。
如果没有现代构建工具,此类依赖项可能会使用简单的 <script>
元素包含在您的项目中,但这可能无法立即使用,并且您可能需要一些现代工具来捆绑您的代码和依赖项在 Web 上发布时。捆绑是一个术语,通常用于指代 Web 服务器上的单个文件,其中包含软件的所有 JavaScript 代码——通常尽可能地压缩以帮助减少下载软件并显示在访问者浏览器中所需的时间。
此外,如果您发现一个更好的工具想要使用而不是当前的工具,或者您的依赖项发布了一个新版本想要更新怎么办?对于几个依赖项来说,这并不是太痛苦,但在依赖项较多的较大项目中,这种事情可能变得非常难以跟踪。使用 **包管理器**(例如 npm)更有意义,因为它可以保证代码被干净地添加和删除,以及其他许多优点。
什么是包管理器?
我们已经遇到过 npm,但从 npm 本身退一步来看,包管理器是一个管理项目依赖项的系统。
包管理器将提供一种方法来安装新的依赖项(也称为“包”),管理包在文件系统中的存储位置,并为您提供发布自己的包的功能。
理论上,您可能不需要包管理器,您可以手动下载和存储项目依赖项,但包管理器将无缝处理安装和卸载包。如果您不使用它,则必须手动处理
- 查找所有正确的包 JavaScript 文件。
- 检查它们以确保它们没有任何已知的漏洞。
- 下载它们并将它们放在项目中的正确位置。
- 编写代码以在应用程序中包含包(这通常使用 JavaScript 模块 完成,这是另一个值得阅读和理解的主题)。
- 对所有包的子依赖项执行相同的操作,这些子依赖项可能数十个或数百个。
- 如果要删除包,请再次删除所有文件。
此外,包管理器处理重复的依赖项(这在前端开发中变得重要且常见)。
在 npm(以及基于 JavaScript 和 Node 的包管理器)的情况下,您有两个选项可以安装依赖项的位置。正如我们在上一篇文章中所提到的,依赖项可以全局安装或本地安装到您的项目中。虽然全局安装往往有更多优点,但本地安装的优点更重要——例如代码可移植性和版本锁定。
例如,如果您的项目依赖于具有特定配置的 Webpack,您需要确保如果您将该项目安装到另一台机器上或稍后返回到该项目,该配置仍然有效。如果安装了不同版本的 Webpack,它可能不兼容。为了减轻这种情况,依赖项会本地安装到项目中。
要查看本地依赖项的真正优势,您只需尝试下载并运行现有项目即可——如果它可以工作并且所有依赖项都可以立即使用,那么您应该感谢本地依赖项使代码可移植。
包注册表
为了使包管理器能够工作,它需要知道从哪里安装包,这以包注册表的形式出现。注册表是一个中心位置,包在其中发布,因此可以从中安装。npm 既是包管理器,也是 JavaScript 包最常用的包注册表名称。npm 注册表位于 npmjs.com。
npm 不是唯一的选择。您可以管理自己的包注册表——像 Microsoft Azure 这样的产品允许您创建到 npm 注册表的代理(以便您可以覆盖或锁定某些包),GitHub 也提供包注册表服务,并且随着时间的推移,可能会出现更多选项。
重要的是要确保您选择了最适合您的注册表。许多项目将使用 npm,并且在模块的其余部分中,我们的示例将坚持使用它。
使用包生态系统
将应用程序设置为 npm 包
首先,创建一个新目录来存储我们的实验应用程序,放在一个您以后可以找到的合理位置。我们将它命名为 npm-experiment
,但您可以随意命名。
mkdir npm-experiment
cd npm-experiment
接下来,让我们将我们的应用程序初始化为 npm 包,这将创建一个配置文件——package.json
——允许我们保存配置详细信息,以防我们以后想要重新创建此环境,或者甚至将包发布到 npm 注册表(尽管这与我们的文章无关,因为我们正在开发应用程序,而不是可重用库)。
键入以下命令,确保您在 npm-experiment
目录中
npm init
现在将向您提出一些问题;然后,npm 将根据答案创建一个默认的 package.json
文件。请注意,这些与我们的目的无关,因为它们仅在您将包发布到注册表并且其他人想要安装和导入它时才使用。
name
:用于识别应用程序的名称。只需按 Return 接受默认值npm-experiment
。version
:应用程序的起始版本号。同样,只需按 Return 接受默认值1.0.0
。description
:应用程序目的的简要说明。我们在这里将省略它,但您也可以输入任何您喜欢的內容。按 Return。entry point
:当其他人导入您的包时,将运行此 JavaScript 文件。这对我们没有用,因此只需按 Return。test command
、git repository
和keywords
:按 Return 将这些都留空。author
:项目的作者。键入您自己的姓名,然后按 Return。license
:发布包所依据的许可证。按 Return 暂时接受默认值。
再次按 Return 接受这些设置。
进入您的 npm-experiment
目录,您现在应该会发现您有一个 package.json 文件。打开它,它应该如下所示
{
"name": "npm-experiment",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Your name",
"license": "ISC"
}
我们将在 package.json 中添加两行
"type": "module"
,这会导致 Node 将所有.js
文件解释为 ES 模块 而不是旧的 CommonJS 模块。养成这个习惯通常是一个好习惯。"private": true
,这可以防止您意外地将包发布到 npm 注册表。
将这些行添加到 "name"
正下方
"name": "npm-experiment",
"type": "module",
"private": true,
所以这是定义您的包的配置文件。这对现在来说已经足够了,所以让我们继续。
安装 Vite
我们首先安装 Vite,这是我们网站的构建工具。它负责将我们的 HTML、CSS 和 JavaScript 文件捆绑成浏览器优化的捆绑包。
npm install --save-dev vite
完成后,再看看您的 package.json 文件。您会看到 npm 添加了一个新字段 devDependencies
"devDependencies": {
"vite": "^5.2.13"
}
这是 npm 魔法的一部分——如果将来您将代码库移动到另一个位置、另一台机器上,您可以通过运行命令 npm install
来重新创建相同的设置,npm 将查看依赖项并为您安装它们。
一个缺点是 Vite 仅在我们的 npm-experiment
应用程序内部可用;您无法在不同的目录中运行它。但优点大于缺点。
请注意,我们选择将 vite
安装为开发依赖项。对于应用程序来说,这种差异很少重要,但对于库来说,这意味着当其他人安装您的包时,他们不会隐式地安装 Vite。通常,对于应用程序,在源代码中导入的任何包都是真正的依赖项,而用于开发的任何包(通常作为命令行工具)都是开发依赖项。通过删除 --save-dev
标志来安装真正的依赖项。
您还会发现创建了许多新文件
node_modules
:运行 Vite 所需的依赖项文件。npm 已为您下载了所有这些文件。package-lock.json
:这是一个锁定文件,存储了重现node_modules
目录所需的确切信息。这确保只要锁定文件不变,node_modules
目录在不同的机器上就会相同。
您无需担心这些文件,因为它们由 npm 管理。如果您使用 Git,则应将node_modules
添加到您的.gitignore
文件中,但通常应保留package-lock.json
,因为如前所述,它用于在不同的机器之间同步node_modules
的状态。
设置我们的示例应用
无论如何,让我们继续设置。
在 Vite 中,index.html
文件处于中心位置。它定义了应用程序的起点,Vite 将使用它来查找构建应用程序所需的其它文件。在您的npm-experiment
目录中创建一个index.html
文件,并赋予其以下内容
<!doctype html>
<html lang="en-US">
<head>
<meta charset="UTF-8" />
<title>My test page</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
请注意,<script>
元素创建了对名为src/main.jsx
的文件的依赖关系,该文件声明了应用程序 JavaScript 逻辑的入口点。创建src
文件夹并在该文件夹中创建main.jsx
,但现在将其留空。
注意:type="module"
属性非常重要。它告诉浏览器将脚本视为 ES 模块,这使我们能够在 JavaScript 代码中使用import
和export
语法。文件扩展名为.jsx
,因为在下一篇文章中,我们将向其中添加 React JSX 语法。浏览器不理解 JSX,但 Vite 会将其转换为常规 JavaScript,就像浏览器理解一样!
使用 Vite 尽情玩耍
现在我们将运行我们新安装的 Vite 工具。在您的终端中,运行以下命令
npx vite
您应该会看到类似以下内容打印在您的终端中
VITE v5.2.13 ready in 326 ms ➜ Local: https://127.0.0.1:5173/ ➜ Network: use --host to expose ➜ press h + enter to show help
现在我们已准备好从完整的 JavaScript 包生态系统中受益。首先,现在有一个本地 Web 服务器在https://127.0.0.1:5173
上运行。您现在还看不到任何内容,但很酷的是,当您对应用程序进行更改时,Vite 将重新构建它并自动刷新服务器,以便您可以立即看到更新的效果。
您可以随时使用Ctrl + C停止开发服务器,并使用相同的命令重新启动它。如果您决定保持其运行,则可以打开一个新的终端窗口来运行其他命令。
现在添加一些页面内容。作为演示,让我们向页面添加一个图表。我们将使用plotly.js包,这是一个数据可视化库。通过运行以下命令安装它
npm install plotly.js-dist-min
请注意,我们是在没有--save-dev
标志的情况下安装的。如前所述,这是因为我们实际上将在源代码中使用此包,而不仅仅是作为命令行工具。此命令会向您的package.json
文件添加一个新的"dependencies"
对象,其中包含plotly.js-dist-min
。
注意:在这里,我们为您选择了包以完成我们的任务。当您编写自己的代码时,在查找和安装依赖项时,请考虑以下问题
- 我是否需要依赖项?是否可以使用内置功能来实现,或者它是否足够简单,可以自己编写?
- 我到底需要做什么?您描述得越详细,就越有可能找到一个完全满足您需求的包。您可以在 npm 或 Google 上搜索关键字。此外,优先选择小型包而不是大型包,因为后者在安装、运行等方面可能会导致性能问题。
- 该依赖项是否值得信赖且维护良好?检查上次发布版本的时间、作者是谁以及该包每周的下载量。确定包的可信度是一项需要经验的技能,因为您必须考虑诸如包需要更新的可能性或可能需要它的人数等因素。
在src/main.jsx
文件中,添加以下代码并保存
import Plotly from "plotly.js-dist-min";
const root = document.getElementById("root");
Plotly.newPlot(
root,
[
{
x: [1, 2, 3, 4, 5],
y: [1, 2, 4, 8, 16],
},
],
{
margin: { t: 0 },
},
);
返回https://127.0.0.1:5173
,您将在页面上看到一个图表。更改不同的数字,并在每次保存文件时查看图表更新。
构建我们的生产代码
但是,此代码尚未准备好用于生产。大多数构建工具系统(包括 Vite)都具有“开发模式”和“生产模式”。重要的区别在于,您在开发中使用的许多有用的功能在最终站点中不需要,因此将在生产中被剥离,例如“热模块替换”、“实时重新加载”和“未压缩且带注释的源代码”。虽然远非详尽无遗,但这些是在开发阶段非常有帮助但在生产中不太有用的常见 Web 开发功能。在生产中,它们只会使您的网站膨胀。
现在使用Ctrl + C停止运行的 Vite 开发服务器。
现在我们可以为一个假设的部署准备我们最基本的示例站点。Vite 提供了一个额外的build
命令来生成适合发布的文件。
运行以下命令
npx vite build
您应该会看到如下输出
vite v5.2.13 building for production... ✓ 6 modules transformed. dist/index.html 0.32 kB │ gzip: 0.24 kB dist/assets/index-BlYAJQFz.js 3,723.18 kB │ gzip: 1,167.74 kB (!) Some chunks are larger than 500 kB after minification. Consider: - Using dynamic import() to code-split the application - Use build.rollupOptions.output.manualChunks to improve chunking: https://rollup.node.org.cn/configuration-options/#output-manualchunks - Adjust chunk size limit for this warning via build.chunkSizeWarningLimit. ✓ built in 4.36s
Vite 将创建一个名为dist
的目录。如果您查看它,它包含一个index.html
,它看起来与根目录中的非常相似,只是script
的源现在被替换为指向assets
文件夹的路径。assets
文件夹包含转换后的 JavaScript 输出,该输出现在已缩小并针对生产进行了优化。
注意:您可能担心有关某个块太大警告。这是预期的,因为我们正在加载一个在幕后执行许多操作的库(想象一下自己编写所有代码来绘制相同的图表)。现在,我们无需担心它。
包管理器客户端简要指南
本教程使用 npm 安装了 Vite 包,但如前所述,也有一些替代方案。至少了解它们的存在并对这些工具的常用命令有一些模糊的概念是值得的。您已经看到了一些实际应用,但让我们看看其他一些。
随着时间的推移,此列表会不断增长,但在撰写本文时,以下主要包管理器可用
- npm 在npmjs.org
- pnpm 在pnpm.js.org
- Yarn 在yarnpkg.com
从命令行角度来看,npm 和 pnpm 非常相似——事实上,pnpm 的目标是完全兼容 npm 提供的参数选项。它的不同之处在于它使用不同的方法在您的计算机上下载和存储包,旨在减少所需的总磁盘空间。
在下面的示例中,如果显示 npm,则可以替换为 pnpm,并且命令将起作用。
通常认为 Yarn 在安装过程中比 npm 快(尽管您的里程可能会有所不同)。这对开发人员来说很重要,因为可能会有大量时间浪费在等待依赖项安装(并复制到计算机上)。
但是,值得注意的是,npm 包管理器**不是**安装 npm 注册表中的包所必需的。pnpm 和 Yarn 可以使用与 npm 相同的package.json
格式,并且可以从 npm 和其他包注册表安装任何包。
让我们回顾一下您希望使用包管理器执行的常见操作。
注意:我们将演示 npm 和 Yarn 命令。它们并非旨在在同一个项目中运行。您应该使用 npm 或 Yarn 设置您的项目,并始终使用该包管理器的命令。
初始化新项目
npm init
yarn init
如上所示,这将提示您并引导您完成一系列问题以描述您的项目(名称、许可证、描述等),然后为您生成一个package.json
,其中包含有关您的项目及其依赖项的元信息。
安装依赖项
npm install vite
yarn add vite
我们还在上面看到了install
的实际应用。这将直接将vite
包添加到工作目录中的名为node_modules
的子目录中,以及vite
自己的依赖项。
默认情况下,此命令将安装最新版本的vite
,但您也可以控制它。您可以请求vite@4
,它为您提供最新的 4.x 版本(即 4.5.3)。或者,您可以尝试vite@^4.0.0
,这意味着 4.0.0 及其之后的最新版本(与上述含义相同)。
更新依赖项
npm update
yarn upgrade
这将查看当前安装的依赖项并在包中指定的范围内更新它们(如果有可用更新)。
范围在您package.json
中依赖项的版本中指定,例如"vite": "^5.2.13"
——在这种情况下,插入符号字符^
表示 5.2.13 及其之后的全部次要和修补程序版本,直到但不包括 6.0.0。
这是使用称为semver的系统确定的,从文档中看起来可能有点复杂,但可以通过仅考虑摘要信息以及版本由MAJOR.MINOR.PATCH
表示来简化,例如 2.0.1 表示主版本为 2,修补程序版本为 1。尝试 semver 值的一个好方法是使用semver 计算器。
务必记住,npm update
不会将依赖项升级到package.json
中定义的范围之外——要执行此操作,您需要专门安装该版本。
更多命令
创建自己的命令
包管理器还支持创建您自己的命令并从命令行执行它们。例如,之前我们使用npx
调用了vite
命令来启动 Vite 开发服务器。我们可以创建以下命令
npm run dev
# or yarn run dev
这将运行一个自定义脚本以在“开发模式”下启动我们的项目。事实上,我们经常在所有项目中包含它,因为本地开发设置的运行方式往往与在生产中的运行方式略有不同。
如果您尝试在您之前的测试项目中运行它,它(可能)会声称“dev 脚本丢失”。这是因为 npm、Yarn(以及类似工具)在您的package.json
文件的scripts
属性中查找名为dev
的属性。因此,让我们在我们的package.json
中创建一个自定义快捷命令——“dev”。如果您按照之前的教程操作,则您的npm-experiment
目录中应该有一个package.json
文件。打开它,它的scripts
成员应该如下所示
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
},
将其更新为如下所示,然后保存文件
"scripts": {
"dev": "vite"
},
我们添加了一个自定义dev
命令作为 npm 脚本。
现在尝试在您的终端中运行以下命令,确保您位于npm-experiment
目录中
npm run dev
这应该会启动 Vite 并启动与之前相同的本地开发服务器。
请注意,我们在此定义的脚本不再需要 npx
前缀。这是因为 npm(和 yarn)命令很智能,它们会在尝试通过常规方法(你的电脑通常存储并允许找到软件的方式)查找它们之前,搜索本地安装到项目中的命令行工具。你可以详细了解 run
命令的技术细节,尽管在大多数情况下,你自己的脚本也能正常运行。
这个特定的命令可能看起来没有必要——npm run dev
比 npx vite
多输入几个字符,但它是一种抽象的形式。它允许我们将来在 dev
命令中添加更多工作,例如设置环境变量、生成临时文件等,而不会使命令变得复杂。
你可以向 scripts
属性中添加各种有助于你完成工作的内容。例如,以下是 Vite 在模板中推荐的内容
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},