作者 | Fernando Doglio
译者 | 王强
策划 | 李俊辰
备受我们信赖的 NPM 上一次迎来重大更新后已经过了一段时间了。终于,在 Node 的最新版本(版本 15)中,我们等到了 NPM 的版本 7。这一版本对其内部架构进行了重大改进,并提供了一些非常有趣的新特性。
在本文中,我会介绍两个引起我注意并激发我的想象力的新特性。第一个特性会改变我们处理所有项目依赖项的方式,而第二个特性会优化一个之前必须手动完成的流程。
当然,我指的就是 工作区(workspaces) 和自动安装 对等依赖项(peer dependencies) 的能力。是不是来兴趣了?反正我心情很激动!
工作区
我最近分享了一篇文章,其中介绍了两种 NPM 客户端,它们都想要解决官方客户端当前实现中的一个主要问题:npm_modules 文件夹已经成为了一个磁盘空间黑洞。
https://blog.bitsrc.io/npm-clients-that-are-better-than-the-original-cd54ed0f5fe7
这两种选项都有自己的独特解决方案,但总体来说它们都会将所有模块保存在一个共享文件夹中,让各个项目之间能够更容易共享软件包。而随着 NPM 最新版本的发布和 Arborist 的引入(一个新项目,包含了负责遍历和分析 npm_modules 文件夹内模块目录树的逻辑),我们看到了官方对这种方法的回应:工作区。
请注意,这并不是一个全新的概念,其他包管理器(例如 Yarn 和 pnpm)已经有了自己的工作区实现。因此,你可以说这只是官方的一个工作区版本而已。
这是什么?
你可以将它视为在预定义和通用上下文内的项目之间共享软件包的一种方式。这并不是说软件包是完全通用的,或者所有内容都要放进同一个下载位置。这个方案确实可以解决一遍又一遍地复制模块的麻烦,还能让你控制我们的模块要共享给哪些项目。
创建工作区后,你就可以明确地告诉 NPM,你的程序包将存放在何处。并且由于新版客户端可以感知工作区,因此它会正确安装依赖项,而不会复制那些通用的依赖。
使用其他包管理器时这个功能也非常有用。例如,可以在单个 NPM 工作区中管理的多个项目之间共享一个 Bit 组件。修改共享组件时,可以用工作区从多个项目中获得即时反馈(查看是否有哪里出现了中断)。
它向后兼容吗?
可惜不行!工作区不是区区配置更改那么简单,它还要求你用新的方式来构造项目。因此向后兼容是做不到的,你不能运行一条 npm 命令就一次性规范化 10 个项目。但是,你可以在重新考虑所有这些项目的结构并正确更改配置之后,将这些项目的依赖项重新安装到一个位置里,这样就可以对所有内容执行重复数据删除操作了。在我看来,这确实是一项巨大的进步!
这个新特性会强制你考虑工作区的实际结构,以及不同项目之间的相互关系,这还会帮助你改善项目的内部组织架构。
考虑下面的例子
有很多介绍工作区的文章,但它们提供的示例在我看来没什么用,所以这里我举一个例子,希望能对 Node.js 开发人员更有帮助。
考虑以下文件夹结构:
基本上,我们将要处理一组 REST API,其中每个 API 的实际代码都会放在“apis”工作区内,而通用代码和共享包将在“core”工作区。
我们如何做到这一点呢?了解了它的工作原理后,配置过程实际上非常简单。这里的重点是你需要在声明工作区的根级别(在 REST-APIS 文件夹内)定义一个 package.json 文件。准备好之后,你要做的就是在每个项目中都创建一个 package.json,并在其中声明其所需的依赖项。
然后,你从根文件夹运行 npm install,让 NPM 完成剩余的工作。下面是根文件夹中的 package.json 文件:
你要做的只有这些,你当然可以定义其他属性,但是对于这个新特性来说,“workspaces”键就足够了。在其中,你可以定义(如示例所示)一个路径列表(还有包含的通配符格式),这些路径引用了工作区所在的文件夹。
在这些文件夹中,你只需声明自己的 package.json 文件,而每个文件都声明它自己的依赖项。
你可以看到,各个 API 文件夹的 JSON 文件实际上区别只有名称和依赖项。core 文件夹也是如此,我们在其中将 Express 声明为其主要共享依赖项:
现在,我们可以从根文件夹运行 npm install,你觉得会发生什么呢?它将在一个通用的 node_modules 文件夹中安装所有这些工作区所需的所有内容。因此,重复的依赖项不会占用多份空间。
有了这些命令和文件夹结构后,你在根目录级别的 node_modules 文件夹中安装了所有三个模块(及其必需的依赖项)。但是,其层次结构内的任何文件都能访问所有这三个文件。
看看 apis/api2/ 文件夹中的这个文件:
const express = require('express')
const app = express()
const port = 3000
const winston = require("winston")
const flat = require("flat")
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example API (#2) listening at http://localhost:${port}`)
})
它用上了所有三个依赖项,而在以前它本来是找不到它们的,这太棒了!
你需要工作区吗?
好吧,如果你正在处理的是单个项目,或者是一些互不相关的项目,那么工作区可能对你来说并没什么用途。它们的需求可能会随时改变,结果让工作区带来的好处烟消云散。
但是,如果你在参与团队中多个相关项目(也许你正在设计一个基于微服务的架构),那么工作区可能会是你非常需要的功能。如果你有一个包含 100 个微服务的架构,所有微服务都依赖同一组模块,那么你可以想想这个新特性会为你节省多少磁盘空间。这个特性的用武之地就是类似这样的场景!
PeerDependencies 自动安装
我今天要介绍的第二大特性就是它。之前我们必须手动安装对等依赖项,以后就用不着了。但是首先,到底什么是对等依赖项呢?
如果你不太熟悉这个术语(以前我也不熟),这里就简单介绍一下:对等依赖项和普通的依赖项几乎没什么区别,它们并没有定义一个严格的要求,而是声明:
理论上讲这都没什么问题,但如果你要自动安装这些依赖项,那么当你添加两个具有相同依赖项但版本不同的软件包时,两个版本就会同时安装(其中一个位于常规的 node_modules 文件夹中,另一个作为需要它的包的子依赖项)。
这可能会导致不兼容的问题,想象一下,我们把依赖模块 B 的模块 A(模块 A 又依赖 React@15)添加到依赖 React@16 的项目中。因为 A 需要版本 15,所以它也将会把版本 15 添加为依赖项,最后依赖项树会变成这个样子:
- React@15
- A
- B
+- React@16
也就是说你得安装两个不同版本的 React,仅仅因为你需要模块 A。但现在我们有了 Arborist,它可以分析整个树并考虑对等依赖,如果出现冲突它就会显示对应的错误信息,并且中止流程。本质上来说,NPM 现在替开发人员完成了这部分工作,并帮助后者决定是否安装这一对等依赖项。考虑到依赖管理的工作也许会浪费一整天的时间,这绝对是一个非常有用的功能。
以前你遇到过这样的坑吗?这个新特性是不是让你非常激动?
NPM 版本 7 已发布,其中包含一些新特性和改进。这两项特性尤其吸引了我的注意,我很快就去尝试它们了。当处理具有多个共享依赖项的大型组合项目时,工作区可以从根本上改善开发人员的体验。
对等依赖管理的改进肯定会为使用基于 NPM 的工具的 React 开发人员带来好处,因为这一特性在这个生态系统中是非常常用的。
你喜欢这些特性吗?还有哪些更新你觉得是很有用的?请在下方留言并分享你的看法。
延伸阅读
https://blog.bitsrc.io/npm-7-this-is-what-i-call-an-update-de17a34ab787