背景
我上半年在工作上做的一些项目架构优化工作,包括:
- 微前端进行了 pnpm + monorepo 的改造,将主应用及多个子应用应用于此架构。
- 将组件库、请求库迁移进上述 monorepo 架构中,并且设计了cicd 流程,只要这些包发生更新,便使依赖这些包的前端服务重新编译部署,也是因为 monorepo,让关联的更新操作自动化,不像以前那样要逐个前端服务下手动
npm i $包名@latest
这般操作使来更新。 - 在请求库中使用了
openapi-ts-request
这个工具,自动生成请求函数,再也不用重复地手写请求函数了 - 组件库里集成了
Storybook
这个在线文档技术来做文档和 demo,不过只是跑通了,还没深入地使用,在组里面推广这项技术也碰到了阻力。
为什么要用 monorepo 架构?
因为部门的项目,前端用了微前端实现的,背景大概是这篇文章,而微前端里面的子应用,其实有很多可以复用的配置文件,包括像:
- Dockerfile、有规律地创建唯一的子应用目录,比如像 /a-web/ 、/b-web/;
- nginx 的配置、每一个前端服务的 nginx 配置里,都要写跨域配置;
- 子应用的通信交互逻辑,包括像子应用接收用户、权限数据,接收要跳转的路由数据
像上述的这些文件,每个子应用都有,而每个子应用又对应一个代码仓库,就比较零散不好管理,对新人也不友好。我认为更合理的做法是,主应用和子应用放在一起,配置集中管理,优化,上下文能更加清晰,开发也更加方便,因为开发也是要主,子应用一起启动的。
为什么要用 pnpm + monorepo 来改造?
因为很久以前就有看到过相关的文章,比方说babel库就是用了lerna 来进行管理,但是可能是本人比较愚笨,好几次尝试着用lerna 来建项目,始终有各种问题,跑不起来,最终还是搁置了。
但是今年发现pnpm竟原生支持monorepo 的特性,而且搭起来异常的简单,所以就上手用 pnpm 来将公司的分散的微前端应用集中起来了。
总结了一些常见用法(持续增加中)
一份pnpm monorepo配置如下:
yaml
packages:
- 'apps/*'
- 'packages/**/*'
catalogs:
vue3:
vue: ^3.2.13
vue-router: ^4.5.0
vuex: ^4.1.0
'@arco-design/web-vue': ^2.57.0
上面的 packages
字段声明了包的位置,比如apps/a-web
,packages/ui/vue-components
,这两个目录下,只要存在package.json 文件,就会被pnpm 认定是一个包
下面的catalogs
.vue3
这个是版本变量声明,可以把它当做是一个依赖的变量,在 a-web/和 b-web
的 package.json
里面,依赖可以这么写:
json
"vue": "catalog:vue3",
"vue-router": "catalog:vue3",
"vuex": "catalog:vue3"
假设,apps/a-web
依赖packages/ui/vue-components
,并且vue-component的包名(package.json
.name
) 是vue-components
,那么在apps/a-web
的 package.json
可以这么写:
json
"vue-components": "workspace:*",
或者也可以用命令加入:
bash
pnpm add "vue-components@workspace:*" --filter a-web
注意,当包含@
符号时,要用双引号包裹包名。
怎么针对某个子包执行 dev 命令,注意这个包的package.json
.name
为 a-web
bash
pnpm --filter a-web dev
怎么对多个应用执行 dev
命令?
bash
pnpm --filter a-web --filter b-web dev
列出当前的pnpm 所有包,可用于快速查看所有包名
bash
pnpm list -r
解决了一些问题:
- Docker 编译速度太慢
在 monorepo 架构下,假设场景为,a-web
前端服务,依赖 services
请求库、vue-components
组件库,那么就会出现下面这种 Dockerfile
写法:
dockerfile
...
COPY pnpm-workspace.yaml ./
COPY pnpm-lock.yaml ./
COPY package.json ./
COPY packages/services ./packages/services
# 假设依赖 请求库, vue-components 组件库
COPY packages/services ./packages/services
COPY packages/ui/vue-components ./packages/ui/vue-components
COPY apps/a-web ./apps/a-web
RUN pnpm install
...
这么写能用,但是有个问题,就是pnpm install
命令这个层,会随着这几个库或者前端服务的代码改变,而始终没办法缓存起来,导致每次编译,都要执行pnpm install
,影响了编译的速度。 优化如下:
dockerfile
...
RUN mkdir -p apps/a-web packages/services packages/ui/vue-components
COPY package.json ./
COPY pnpm-workspace.yaml ./
COPY pnpm-lock.yaml ./
COPY apps/ds-server-gateway2-web/package.json ./apps/ds-server-gateway2-web/package.json
COPY packages/services/package.json ./packages/services/package.json
COPY packages/ui/vue-components/package.json ./packages/ui/vue-components/package.json
RUN pnpm install --registry $私有仓库地址
COPY packages/services ./packages/services
COPY packages/ui/vue-components ./packages/ui/vue-components
COPY apps/a-web ./apps/a-web
...
也就是前置各个包的 package.json
拷贝操作,pnpm install
操作,来命中Docker
的层缓存机制。
持续更新中。