微前端在基础架构应用系列的实践
背景
基础架构组在搭建一套由许多个应用组成的系列应用,共享同一套用户体系(包含鉴权登录、权限管理、企业微信组织架构)。
目前的应用大致有:
- 员工管理平台
- 数据库管理平台
- 运维自动化平台
- 项目发布平台(k8s)
- 微服务、定时任务管理平台
- …
这就涉及到了用户体系的共享方案。
- 做一个登录服务和导航页,统一在
login.xxx.com
下完成登录,并跳转回对应的网址。 - 微前端,主应用完成登录,使用顶部导航菜单在同一个网页来切换微应用,将用户信息下传。
由于这些平台在前端框架选择上比较类似,决定使用 Umi.js,统一风格,并且我们需要可以非常方便地根据登录用户,在导航中插拔可访问的微应用。
最后选择了 Umi.js 自带的微前端(乾坤插件)。
主应用
1. 概述
主应用只需要两个模块:
- 登录(获取到 token)
- 获取用户权限信息(一棵树)
考虑到主应用的轻量,不需要太多框架型的东西存在,最后选择用 CRA + 乾坤 来搭建。
2. 通过接口获取到需要注册的微应用,并挂载
确定一个挂载微应用的 DOM
1 | const LAYOUT_ROUTES: { |
通过接口获取到的 entries 动态挂载微应用
1 | import { registerMicroApps, start } from 'qiankun'; |
3. css 优化
这个项目里暂时不打算用 ShadowDom 来挂载微应用,所以不打开乾坤的沙盒模式。主应用的样式采用 css-in-js 的方案来隔离。最后选择了 emotion。
微应用
1. 接入
微应用统一用了 Umi.js 所以接入起来非常方便。
第一步:在 .umirc.js
或 config.js
修改框架配置的地方添加
1 | export default defineConfig({ |
第二步:app.tsx
中添加生命周期钩子
1 | export const qiankun = { |
这样基本上就可以联通了,然后做一些界面的更新。
微应用本身也是有顶部菜单栏的,有logo和头像,如果通过主应用打开,那就不需要自身的菜单了。
在 app.tsx
中添加:
1 | export const layout: RunTimeLayoutConfig = ({ initialState }) => { |
通过 window.__POWERED_BY_QIANKUN__
来判断是否显示自身的 Header。
最后,微应用是通过二级域名部署的:
- fengchu.baixing.cn (主)
- admin.fengchu.baixing.cn (微)
- cloud.fengchu.baixing.cn (微)
- …
所以通过域名作为入口会跨域,有两种方案解决:
在主应用的镜像里用 nginx 反代,然后用 /fengchu-admin-app 作为入口
1
2
3
4
5location /fengchu-admin-app {
proxy_pass http://admin.fengchu.baixing.cn;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Requested-For $proxy_add_x_forwarded_for;
}在微应用中的镜像里 nginx 添加返回头,允许主应用域名跨域访问:
1
2
3
4
5
6
7
8
9
10
11location / {
add_header Access-Control-Allow-Origin http://fengchu.baixing.cn;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
try_files $uri $uri/ /index.html;
}
都行吧,第一个会有路径冲突的风险,第二个需要在每个微应用中都配置。
2. 接收用户和权限信息
主应用中,在注册微应用的时候,通过 props 传入了准备好的用户信息数据 profile:
1 | registerMicroApps([ |
profile 来自全局状态管理 profile model(用的哪个工具不重要),所以带有监听,只要有更新此处的 props 就会随即更新。
微应用中,创建一个类用来存放用户信息:
1 | import { currentUser } from '@/services/fengchu/api'; |
在 getUser() 中,如果事先已经接收到主应用的profile,则直接返回,如果没有(单独打开微应用)则通过 api 获取后存入。
在 bootstrap 钩子里获取父级传参:
1 | async bootstrap(props: any) { |
总结
其实很多场景下根本不需要用到微前端。(你可能不需要微前端)
但为了尝鲜,也不是,为了可以方便地通过权限来管理可访问应用,并让所有系列应用共享一套用户体系,加上乾坤提供的近乎躺平的落地方案,就采用了。
由于没有用 ShadowDom 去隔离样式,微应用的主题色还能覆盖主应用的,效果还行。