Umi 是一个很棒的 React 框架用于快速搭建项目。目前 Umi 4 已经发布,此版本自带了社区中比较常用的特性(包括:tailwindcss、monorepo、MFSU V3、React Router 6 等)。 其中 monorepo 是期待很久的特性,遗憾的是 3.x 版本现在以及未来都不持 monorepo。问题是实际的项目中或多或少会有多个项目依赖相同代码和组件的需求,我喜欢用 monorepo 来共享组件、工具方法等。
当然发布成 npm 包也是一种方案,只是我偏好用 monorepo
因为老项目都跑在 Umi 3.x 版本,怎么在不支持 monorepo 的项目中实现模块共用就是我要分享的内容。
目录结构
apps
--packages
--app1 # umi 项目 1
--app2 # umi 项目 2
--service # 网络请求
--utils # 工具方法
--package.json
上面的目录结构可以看出 app1 & app2 想要共享 service & utils 包中的代码,在 Umi 3.x 版本中 debug 源代码发现只要在 building time 时把 service & utils 的目录路径添加到 webpack 的“查找目录列表”即可实现类似于 monorepo 的效果。
具体实现
- 在 service 目录中创建 plugin.js 文件 利用 Umi 的 plugin 机制可以将 service 的目录路径注入到 webpack 的各个阶段
// service/plugin.js
export default (api)=>{
api.chainWebpack(webpackConfig => {
webpackConfig.resolve.alias.set('@service', path.resolve(__dirname));
// 将 service 目录中的代码用 ts-in-node_modules rules 解析,以解决 loader 找不到的问题
webpackConfig.module.rule('ts-in-node_modules').include.add(path.resolve(__dirname)).end();
return webpackConfig;
});
}
- 在 app1 中配置 .umirc.ts 文件 plugin 和 chainWebpack 选项
// app1/.umirc.ts
import { resolve } from 'path'
export default defineConfig({
// 启用 service 插件
plugins: [resolve(__dirname, './../service/plugin.js')],
// umi build 时将 app1/node_modules 添加到 webpack modules 源路径中,
// 以解决当引用 service 时引用到的第三方包时报找不到的问题
chainWebpack(config) {
config.resolve.modules.add(`${resolve(__dirname)}/node_modules`).end()
}
})
- 在 app1 代码中引用 service 代码即可
// app1/pages/products/index.tsx
// 从 service 包中引用 `fetchProducts` 方法
// 注意这里要用 @services alias
import { fetchProducts } from "@service/product"
const ProductsPage = (props) => {
const [products, setProducts]=useState([])
useEffect(()=>{
fetchProducts().then((data)=>{
setProducts(data)
})
}, [])
return (
<pre>
{JSON.stringify(products)}
</pre>
)
}
export default ProductsPage
- 在 app1 目录中配置 tsconfig.json 以解决编辑器能索引到文件
// app1/tsconfig.json
{
{
"compilerOptions": {
// ....
"paths": {
"@service/*": ["./../service/*"],
//....
}
}
}
经过以上的代码调整基本上能满足在 Umi 3.x 中实现 monorepo 级别的代码共用。