这也让我很困惑,我做了一些研究,查看了react-scripts版本4.0.3的源代码。此版本需要babel-loader v. 8.1.0。安装了storybook(需要版本^8.0.0),我们在顶层node_modules 中得到一个不同的babel-loader,因此我们最终得到:
|-src
|-node_modules
|
|-storybook 6.3.12
|-babel-loader 8.2.6
|-react-scripts 4.0.3
|
|-node_modules
|
|-babel-loader 8.1.0
我收到与很多人看到的相同的错误消息,我想我自己,不应该在react-scripts 中使用babel-loader 在它自己的babel-loader 中获得babel-loader,即版本8.1.0?我可以多次删除node_modules 和package-lock.json,而且问题似乎一直存在。
您在问题中如何描述它实际上是包管理器(至少npm)如何工作,例如从react-scripts 导入babel-loader 会从它自己的node_modules 给它版本8.1.0...但是我们忘记了两件事,它们结合起来会导致问题:
吊装
我不会详细说明何时发生包裹提升,但它确实发生了。基本上,它相当于将依赖包A 的依赖包B 添加到项目根node_modules(或其他一些父node_modules),而不是添加到A 的node_modules。
所以不是
|-src
|-node_modules
|
|-A
|
|-node_modules
|
|-B
...我们得到...
|-src
|-node_modules
|
|-A
|
|-node_modules (might exist anyway)
|-B
由于npm 的工作方式(并且正如您在问题中正确理解的那样),如果在node_modules/A/node_modules 中找不到B,它将在node_modules 中查找包,这使得它可以工作。当许多包都需要兼容的依赖项时,提升可以是一种优化,这样我们就可以只存储一个,而不是存储 N 个相似版本的依赖项。它还简化了项目根目录node_modules 的文件夹结构,从而更易于调试。事实上,默认是吊起任何可以吊起的包,例如node_modules 中尚不存在要提升到的对象。
通常,每当我们有冲突的包版本时,都会使用“包的所有依赖项都在它自己的node_modules”中的天真的策略。这就是上面例子中发生的情况,有两个不同版本的babel-loader; storybook 确实由几个包组成,其中多个包使用babel-loader 版本^8.0.0。当storybook 安装在react-scripts 之前时,它会采用满足约束的babel-loader 的最新版本,并将其提升到顶层node_modules。这会在安装react-scripts 时导致常见的问题。我有另一个具有相同设置的项目,但是在storybook 之前安装了react-scripts。在那里,babel-loader 版本 8.1.0 被吊起,并且由于这个包也满足 storybook (^8.0.0) 的要求,因此不再需要 babel-loader。在这里,create-react-scripts 没有抱怨。当然,npm 可以自己计算出这个顺序当然是可取的,这从存储的角度来看也是最佳的(只有一个 babel-loader 而不是两个),但据我所知,npm 使用字母顺序时处理依赖关系。
传递依赖
尽管包 A 是我项目的依赖项,A 也可以拥有自己的依赖项。这些是关于我的项目的传递依赖项。
为什么它不起作用?
在react-script 版本4.0.3 的源代码中,文件verifyPackageTree 负责运行检查帽,导致问题中出现无聊的错误消息。这个文件在以后的预发布中不再存在,但在它的顶部,我们可以找到评论:
// We assume that having wrong versions of these
// in the tree will likely break your setup.
// This is a relatively low-effort way to find common issues.
下面几行写着:
// These are packages most likely to break in practice.
// See https://github.com/facebook/create-react-app/issues/1795 for reasons why.
// I have not included Babel here because plugins typically don't import Babel (so it's not affected).
查看引用的线程,讨论了为什么create-react-app在安装不兼容的依赖项时会中断,问题与上述相同... 为什么不起作用?
最后的结论似乎是,例如,如果react-scripts 导入一个已被提升的包X,则该包不会驻留在react-scripts 下的node_modules 中,而是位于根@ 987654389@,其中(相对于最上面的示例)还有babel-loader 版本8.2.6 驻留(与react-scripts 的node_modules 文件夹中的版本8.1.0 相对)。现在如果 X 要导入 babel-loader,它会得到错误的 babel-loader,例如不是预期的版本8.1.0,而是8.2.6。在线程中,这被认为是npm 中的一个错误,即使我不完全知道为什么它会是一个错误。也许,可能会有一些标志表明是否可以吊起一个包裹,因为如果完全吊装被认为是一个错误,这似乎是一种倒退。
因为create-react-app 的编写者不知道某些依赖项是否被提升,所以他们实现了这个简单的检查,它会抛出一个警告。对于高级用户,他们有机会使用SKIP_PREFLIGHT_CHECK 标志选择退出检查。另外,最好记住错误来自create-react-app,而不是npm,所以它实际上并没有说明npm 的工作原理,只是create-react-app 的创建者认为的问题(这是最初确实同样令人困惑)。