-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
深入理解 yarn 中的 nohoist 机制 #72
Comments
"workspace-aggregator-1917a6ce-f14f-4b68-a050-0f6f7e8482d9 > gl-unified-permission > [email protected]" has unmet peer dependency "webpack@^4.0.0 || ^5.0.0". 安装多个项目提示的,有影响吗 |
看起来都是warning,应该没有影响 |
能禁止某一个包里面的依赖吗? |
@Lovercz ,我也不太清楚。你如果有好的解决方案,也可以告诉我哈😀 |
A依赖B,B依赖C |
同问 |
Good job |
确实,node_modules 依赖树是复杂、多层的。类似 vue-cli、create-react-app、react 等这类依赖较为确定的项目适合去做Monorepo。 而庞大的业务项目集中,不同项目的依赖做组成的依赖树在workpaces的组织下不见得是稳定的。 比如某个package-A, 其package.json如下: {
"devDependencies": {
"vite-plugin-svg-icons": "^2.0.1",
"vite": "^2.8.0"
}
} 而 {
"peerDependencies": {
"vite": ">=2.0.0"
}
} 此时包含其他package-B 、 package-C, 它们有共同依赖: {
"devDependencies": {
"vite": "^4.8.0"
}
} 此时通过workspace 安装后 - <rootDir>
- packages
|- package-A
|_ node_modules
|_ vite ^2
|- package-B
|_ package-C
- node_modules
|- vite ^4
|_ vite-plugin-svg-icons ^2
这时导致 vite-plugin-svg-icons 引用的vite是 诸如此类的依赖问题可以展开。 已经在业务项目中弃用这种模式了 |
感觉真正能用在生产中的两组模式:pnpm(推荐的)or lerna + yarn (nohoist 模式) |
当前的问题是什么?
首先,让我们先快速回顾一下在一个独立项目中的
hoist
工作机制:为了减少项目中的依赖包冗余,很多项目开发者会使用某种
hoist
机制将公共的依赖包提取出来,并将其展开集中到同一个目录下。下图中左边的示例是一个常见的普通项目依赖树结构:
通过使用
hoist
机制,我们可以尽可能消除对[email protected]
和[email protected]
两个包的重复依赖安装,同时还不会改变对[email protected]
的依赖维护关系。而我们知道,大多数模块爬虫、loaders和打包器都是通过遍历项目中的node_modules
来定位依赖包的。那么在
monorepo
类型的项目中,引入了一种新的层级结构,它不再需要必须使用node_modules
来建立模块间的依赖关系。在这种项目中,模块可以分散在项目中的多个位置:yarn workspaces
可以通过将公共依赖包提升到所有子项目的父目录的node_modules
中,以实现共享这些依赖包的机制。而且如果这些依赖包彼此之间也有依赖关系时,使用这种优化方式的好处会更优。无法找到模块!
并不是所有的模块爬虫都会遍历模块的软链接。因此,有可能我们在每个子项目中进行编译打包时,会出现模块无法找到的情况。
看上图右侧的项目结构,我们在实际打包时,可能会出现以下情况:
monorepo
根目录无法找到[email protected]
依赖包。因为[email protected]
存在于package-1
的node_modules
下,而它是以软链接的形式存在于monorepo
根目录的node_modules
下。package-1
无法找到[email protected]
模块。因为[email protected]
实际存在于monorepo
根目录中的node_modules
下。在这个
monorepo
项目中,依赖包可能存在于任意位置。它就需要模块爬虫能够遍历每一个node_modules
目录才可能找到对应的模块。为什么这些模块的位置不能固定下来呢?
事实上有很多开发都提出过此类问题的解决办法,例如:可以建立多个根目录、可以自定义模块映射、更智能的模块遍历模式等。但不管怎样,还是有一些原因使得问题难以解决:
什么是
nohoist
?那有没有一种简单的机制能够使这些不兼容的库可以正常在 monorepo 中工作呢?
那就是
yarn
提供的nohoist
机制,这种机制在lerna
中也有相关演示。nohoist
机制可以使workspace
去自定义处理那些不兼容hoist
模式的第三方库。只要你进行了配置,它就不会把这些再模块提升到根目录。它们还是被放在原来的子项目中,就像运行在一个标准的、没有workspace
的工程一样。一些注意事项
nohoist
虽然很有用,但是它还是有一些缺点。最明显的就是指定的nohoist
模块依然会被重复安装在多个目录中,这也就完全违背了上面我们提到的多种好处。建议在指定
nohoist
的范围时,尽量越小、越精确越好。如何使用它?
nohoist
的使用方法非常简单。在package.json
中进行定义相应的规则即可。yarn
是从1.4.2
版本开始提供此功能的。下面是它的类型定义:
使用示例:
nohoist
支持使用 glob 语法匹配模块路径依赖目录。模块目录是一个虚拟目录,不用带上node_modules
和packages
等路径。进一步说明
让我们通过下面一个伪项目结构,来说明
nohoist
机制是如何阻止react-native
模块被提升的。在下面的monorepo
项目中主要有 A、B、C 三个子项目:在执行
yarn install
命令以前,它们的文件目录结构如下:package.json
文件们于monorepo
的根目录下private
属性nohoist
和workspaces
只能在private: true
的项目中工作。在
yarn
内部,它会基于每个模块的原始依赖包(在被提升之前的结构)构建一个虚拟的模块路径。如果nohoist
匹配到了其中的路径,它就会被离其最近的子项目所代替。在 A 项目中:
在 B 项目中:
在 C 项目中:
nohoist
模式**/react-native
,这会告诉yarn
不要去提升react-native
模块,不管它的位置在哪里。**/react-native/**
,这会告诉yarn
不要去提升react-native
的任何依赖包。这两种模式结合起来就是告诉
yarn
不要去提升react-native
以及它的所有依赖包。如何关闭
nohoist
?只要在一个私有的
package.json
中添加了nohoist
配置,yarn
默认就会使用它。如果想关闭
nohoist
,一般有三种方式:package.json
中移除nohoist
的相关配置。.yarnrc
中添加workspaces-nohoist-experimental: false
标识。yarn config set workspaces-nohoist-experimental false
命令来关闭它。原文资料
The text was updated successfully, but these errors were encountered: