在过去,一个简单的文本编辑器就足以让开发人员创建和管理大部分项目。但从那以后,WEB发生了翻天覆地的变化,如今,即使是一个相当简单的项目,通常也会有成百上千个带有复杂嵌套依赖关系的脚本,如果没有自动化工具,这些脚本根本无法有序的管理,这时就需要包管理器。
包管理器是一种以各种方式自动处理项目依赖关系的工具。例如,在包管理器的帮助下,可以安装、卸载、更新和升级包,配置项目设置、运行脚本等等。所有复杂和乏味的工作都由包管理器完成,让开发人员专注于编码。
npm 是Node的包管理器,它于2010年发布,开启了web开发的新时代。在此之前,项目依赖库都是手动下载和管理的,npm 是把WEB开发推向了一个更高的阶段。
npm 主要做三件事:
- 一个用于管理 npm 体验各个方面的网站
- 用于访问广泛的 JavaScript 包公共数据库的注册表
- 用于通过终端与 npm 交互的命令行界面(CLI)
然而,大多数人谈论 npm 时,通常指的是最后一个 CLI 工具。它作为默认包管理器与每个新的 Node版本一起发布。
yarn 代表另一个资源谈判者。yarn 包管理器是 npm 的一个替代方案,由Facebook于2016年10月发布。yarn最初的目标是处理npm的缺点,比如性能和安全问题。yarn很快被定位为一个安全、快速、可靠的JavaScript依赖管理工具。
但是 npm 团队吸取了教训,并通过实现缺失的功能迅速填补了 npm 的空白。
下面来看一个时间线:
- 2010年:发布了支持Node的npm。
- 2016年:Yarn发布。它展现了比npm更好的性能。它也会生成yarn.lock 文件,使共享和精确复制回购更容易和可预测。
- 2017年:NPM 5发布。它提供自动生成的包锁 package-lock.json 文件应对 yarn.lock。
- 2018年:NPM 6的发布提高了安全性。现在,npm在安装依赖项之前会检查安全问题。
- 2020年:Yarn 2和npm 7发布。这两个包都有很棒的新特性。
- 2021年:Yarn 3发布了各种改进。
如今,这两种包管理器在包管理竞赛中并驾齐下,提供了相似的特性和功能。但仍有一些差异,有助于选择使用。
安装比较
从 npm 和 yarn 的安装过程开始来进行比较。
安装包管理器
正如上面提到的,npm 是预先安装在 Node 中的,所以一般不需要手动安装 npm。
相反,yarn 需要显式安装,首先,需要全局安装 yarn:install -g yarn 然后,可以通过在项目中设置所需的版本,在每个项目的基础上使用它。通过在项目的根目录中运行 yarn set version 命令来设置所需要的版本:set version berry berry 就是要设置的版本号。如果想更新到最新版本,运行:set version latest 使用 yarn,可以为每个项目使用不同的版本。而对 npm 要实现同样的需求,则需要安装 nvm(Node版本管理器)。
安装项目依赖
现在,就来看看如何安装项目依赖项。 当运行 npm install 时,依赖项会依次安装,终端中会输出详细的安装日志,不过阅读性不好。
使用 yarn 安装包,运行 yarn 命令。yarn 是并行安装包的,这也是它比 npm 快的原因之一。如果正在使用 yarn 1,将看到 yarn 输出的安装日志比较简洁,阅读性也比较好。为了方便阅读,它们以树状排列。但是这在版本2和版本3中有所改变,其中的日志不是那么直观和可读。
到目前为止,已经看到 npm 和 yarn 有不同的安装包命令。
命令比较
npm 和 yarn 很多命令是一样的,但也有许多不同的命令。先来看看相同的命令:
- npm init | yarn init:创建一个新包
- npm run | yarn run:运行 package.json 中定义的脚本
- npm test | yarn test:测试一个包
- npm publish | yarn publish:发布一个包
- npm cache clean | yarn cache clean:从缓存文件夹中删除所有数据
这些命令使两个管理器之间的切换变得容易,但有一些不同的命令可能会导致混淆。
- npm install | yarn:安装依赖
- npm install [package] | yarn add [package]:安装一个包
- npm install --save-dev [package] | yarn add --dev [package]:安装包作为开发依赖项
- npm uninstall [package] | yarn remove [package]:卸载一个包
- npm uninstall --save-dev [package] | yarn remove [package]:卸载开发依赖包
- npm update | yarn upgrade:更新的依赖关系
- npm update [package] | yarn upgrade [package]:更新包
yarn 还有一些独特的命令,这些命令在 npm 下没有相同的。例如,why 命令显示需要一个包的原因:它可能是一个依赖项、一个本地模块或一个项目依赖项。
速度和性能
每当 yarn 或 npm 需要安装包时,它们都会执行一系列任务。在 npm 中,这些任务是按包顺序逐个执行安装的,这意味着它会等待一个包完全安装,然后再继续下一个。相比之下,yarn 是并行执行这些任务,在性能上有显著的提高。
虽然这两种管理器都提供缓存机制,但yarn似乎做得更好一些。通过实现零安装模式,它几乎能够在短时间内安装包。它缓存每个包并将其保存在磁盘上,所以在下一次安装这个包时,甚至不需要有互联网连接,因为包是从磁盘离线安装的。
尽管yarn有一些优势,但 yarn 和 npm 在它们的最新版本中的速度相当,所以现在算不分伯仲。
安全性比较
对 npm 的主要批评之一是在安全性方面,以前的 npm 版本有几个严重的安全漏洞。然而从版本 6 开始,npm 在安装过程中审核软件包并显示是否发现任何漏洞。可以通过对已安装的软件包运行 npm audit 来手动执行此检查,如果发现任何漏洞,npm 会给出相应的安全建议。如发现有安全漏洞,可以运行 npm audit fix 来修复包漏洞。
在安全性上,yarn 和 npm 都使用加密哈希算法来确保包的完整性。
功能比较
就如上面介绍的命令一样,一些特性是 npm 和 yarn 共有的,但也有一些区别,下面就来介绍主要的区别。
生成的锁定文件
在package.json 文件中,npm 和 yarn 都在其中跟踪项目的依赖项,版本号并不总是准确的,相反,可以定义一系列版本。这样,可以选择一个包的主版本和小版本,但允许npm安装可能修复一些bug的最新补丁。
在语义版本控制的理想状态下,补丁版本不会包含任何破坏性的更改。但显示总是和理想有区别,导致真实情况下并非如此。npm 采用的策略可能会导致两台机器以相同的 package.json 文件结束,但安装了不同版本的包,这就埋下了可能产生bug的问题。
为避免包版本不匹配,确切安装的版本被固定在包锁定文件中,每次添加模块时,npm 和 yarn 分别创建(或更新)一个 package-lock.json 和 yarn.lock 文件。
使用工作空间
工作区允许拥有一个 monorepo 来管理跨多个项目的依赖项,这意味着有一个单一的顶级根包,其中包含多个称为工作区的子包。
远程运行脚本
npx 命令用于从 ./node_modules/.bin 运行脚本。它还允许从 npm 注册表中执行包,而无需将它们安装在项目依赖项中。例如,可以通过运行以下命令来创建一个新的 React 应用程序:create-react-app my-app 在 yarn 中,可以使用等效的 dlx 命令获得相同的结果:dlx create-react-app my-app 下面将介绍 yarn 独有的功能。
零安装
零安装将缓存存储在项目目录中的 .yarn 文件夹中。当使用 yarn 或 yarn add <package> 等命令时,yarn 会创建一个 .pnp.cjs 文件,此文件包含 Node 用于加载项目包的依赖关系层次结构。因此,几乎可以在零时间访问它们。
即插即用
即插即用是另一种安装策略,yarn 没有生成 node_modules 目录并将解析留给 Node,而是生成单个 .pnp.cjs 文件,该文件将包映射到它们在磁盘上的位置及其依赖项列表。这个特性可以导致更快的项目启动、更好的优化依赖树、更快的安装时间,当然也不需要 node_modules 文件夹。
Licenses
yarn 包含一个内置的许可证检查器,可在开发应用程序时在不同场景中使用。
选择哪个包管理器
上面已经讨论了 npm 和 yarn 的各种相似和不同之处,但是还没有确定哪一个更好,应该选择哪一个,但是还是那句话,适合的团队或者项目的才是最重要。
下面给出一个推荐的建议:
- 选择 npm :如果对当前的工作流程感到满意,不想安装额外的工具,并且没有很多磁盘空间。
- 选择 yarn :如果想要一些很棒的功能,比如即插即用,需要一些 npm 中缺少的功能,并且有足够的磁盘空间
如果仍然很难在 npm 和 yarn 之间做出明确的决定,那就无需在意,随便用那一个都基本可以满足项目开发要求。
总结
包管理器对现代 web 开发非常的重要,本文比较了市场上两个最受欢迎的包管理器,它们都有各自的优点和缺点,选择最适合项目的。</div>
<div id="asideoffset"></div>
|