monorepo
中 hoist
机制导致加载配置文件路径的变化作为一名前端,我们经常会在项目里添加大量的配置文件,用来配置各种各样的库和开发工具。
比如配置 typescript
,我们会添加 tsconfig.json
。
配置 npm
,yarn
,pnpm
这种包管理工具,我们会添加 .npmrc
。
配置 eslint
,我们要配置 .eslintrc.js
,.eslintignore
...
配置格式化,要配置 .editorconfig
,.prettierrc
...。
配置 git
的 .gitignore
,.gitattributes
配置 jest
的 jest.config.ts
babel
-> babel.config.js
postcss
-> postcss.config.js
webpack
-> webpack.config.js
.env
,.env.dev
,.env.sit
,.env.prod
,.env.local
vite.config.[jt]s
,vue.config.js
,lerna.json
,rollup.config.[jt]s
,stylelint.config.js
,tailwind.config.js
,.browserslistrc
,serverless.yml
,Dockerfile
,nginx.conf
...
看到这么多配置文件,只能说前端好累,工作好苦,学不动躺平了。
在通常情况下,上述这些文件,一般是放在项目的根目录下。此时它的默认作用域是对整个项目生效了。
但是在 monorepo
中,还是这么的简单方便吗?显然不是的。
这里我们以一个前端 yarn monorepo
为例:
// package.json
"workspaces": {
"packages": [
"packages/*",
"apps/*"
]
}
其中 packages/*
中存放着所有的scope
包,apps/*
表示所有的应用。
在 apps/*
中有 2
个项目,一个是 pc端
->WebClient
,另一个是移动端
->MobileClient
(以下简称Web
和Mobile
)。
显然,这两个项目的配置文件,有着极大的不同。比如 postcss.config.js
这个配置文件,我们在Mobile
中引入了 viewport
自适应方案的插件,而Web
端不需要,这造成了些许差别了。
此时显然把 postcss.config.js
放在 monorepo
的根目录是不恰当的,它需要跟着项目走。所以我们就可以在Web
和Mobile
中各放一个,再通过 preset
这种思想的方式,提炼公共插件即可。这时候配置文件的作用域就缩小,变成了 Web
和 Mobile
目录了。
假如有人问,为啥要分成 2 个端这样做,不能一套自适应吗?我的回答实际上也是可以的,比如你仅仅只需要把
postcss.config.js
变成Function
,然后判断一下传入路径,返回不同的配置即可,其余情况亦然。
hoist
机制下的问题hoist
机制,npm
/yarn
/pnpm
都有,它能够把多个 repo
中版本不冲突的共用模块,会被尽可能地提升至根路径。
在 yarn
默认开启 hoist
后,我们可以看到,Web
和 Mobile
安装的包,都被保存在了 根目录
下的 node_modules
中。
此时应用似乎能正常运行。
接下来我们分别在 Mobile
和 Web
安装一些 UI Toolkit
包,同样,它们被提升到了根目录的 node_modules
中。但它们也能被正常的使用。
但是,由于 hoist
机制,UI Toolkit
包被提升后,它们不在项目的 postcss.config.js
作用范围内!
此时你使用 postcss-loader
去处理,内部的 postcss-load-config
加载到的配置文件,实际上是一个空对象!!!
如图所示:
监听 postcss-loader
中的 load-config
部分代码
注意加载的上下文和 load-config
注意 hoist
后的文件路径和 load-config
此时,假如有需求,要让 postcss
也走这样的配置,主要有 2
种方式:
UI Toolkit
的提升机制postcss.config.js
显然,方法一比较符合我们的本意,所以我们需要做出如下修改:
"workspaces": {
"packages": [
"packages/*",
"apps/*"
],
"nohoist": [
"**/vant",
"**/element-plus"
]
}
这样就解决了这个问题。
同样这个机制也能解决 monorepo
中,多 vue
/react
版本的问题:
"workspaces": {
"packages": [
"packages/*",
"apps/*"
],
"nohoist": [
"**/react",
"**/vue"
]
}
这样你就可以愉快的同时启动 vue2
和 vue3
项目了。