微前端
最近在着手重构整个公司的大后台,目前情况,后台项目过多,所使用的技术栈都不一致
刚好有机会实践微前端技术
本文写法简洁,旨在记录实现过程,并未记录详细细节,敬请谅解
后续会记录一系列遇到的坑点和问题
qiankun Demo
1、主应用
经过一番技术的battel,最终决定基座采用ant-design-pro开箱即用,非常方便
1、使用ant-design-pro搭建主项目
使用官方脚手架
2、引入@umijs/plugin-qiankun npm包
3、config.ts配置文件加入qiankun的配置
涉及到菜单以及权限控制,则采用动态加载子应用和路由的思路
qiankun: {
master: {}
}
4、采用组件引入的方式
在layous文件夹新建MicroAppLayout.tsx
import { MicroApp } from 'umi';
import React from 'react';
import { PageContainer } from '@ant-design/pro-layout';
function MicroAppLayout() {
const name = location.pathname.split('/')[1];
console.log(name)
return (
<PageContainer>
<MicroApp name={name} />
</PageContainer>
)
}
export default MicroAppLayout;
5、添加动态mock数据
注:暂未实现接口,所有数据来源于mock
在mock文件夹新建config.ts加入以下代码
export default {
'/api/config': {
apps: [
{
name: 'sub-app-1',
entry: '//localhost:8001',
},
{
name: 'sub-app-2',
entry: '//localhost:8002',
},
],
routes: [
{
name: 'sub-app-1',
path: '/sub-app-1',
routes: [
{
name: 'sub-app-1-first',
path: '/sub-app-1/first',
},
{
name: 'sub-app-1-second',
path: '/sub-app-1/second',
}
]
},
{
name: 'sub-app-2',
path: '/sub-app-2',
}
],
},
};
6、动态加载子应用
在src目录添加app.tsx文件
动态加载子应用代码如下:
export const qiankun = fetch('/api/config')
.then((res) => {
return res.json();
})
.then(({ apps }) => {
return Promise.resolve({
// 注册子应用信息
apps,
lifeCycles: {
afterMount: (props: any) => {
console.log(props);
},
},
// 支持更多的其他配置,详细看这里 https://qiankun.umijs.org/zh/api/#start-opts
});
});
7、动态加载路由
export function patchRoutes({ routes }: IRouteObj) {
extraRoutes.forEach((element: IElementItem) => {
routes[1].routes[0].routes.unshift({
name: element.name,
icon: 'smile',
path: element.path,
component: dynamic({
loader: () =>
import('@/layouts/MicroAppLayout'),
loading: LoadingComponent,
}),
routes: element.routes
});
});
}
export async function render(oldRender: Function) {
fetch('/api/config')
.then((res) => {
return res.json();
})
.then((resJson) => {
extraRoutes = resJson.routes;
oldRender();
});
}
2、子应用
1、添加子应用,并暴露子应用周期方法
react子应用
export const qiankun = {
// 应用加载之前
async bootstrap(props) {
console.log('app1 bootstrap', props);
},
// 应用 render 之前触发
async mount(props) {
console.log('app1 mount', props);
},
// 应用卸载之后触发
async unmount(props) {
console.log('app1 unmount', props);
},
};
vue子应用
1)修改Vue项目的入口文件main.js
let router = null
let instance = null
function render() {
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? '/live' : '/',
mode: 'history',
routes,
scrollBehavior (to, from, savedPosition) {
return { x: 0, y: 0 }
}
});
instance = new Vue({
router,
store,
render: h => h(App)
}).$mount('#app');
}
if (!window.__POWERED_BY_QIANKUN__) {
render()
}
export async function bootstrap() {
console.log('vue app bootstraped');
}
export async function mount(props) {
console.log('props from main app', props)
render()
}
export async function unmount() {
instance.$destroy()
instance = null
router = null
}
2)引入webpack publicPath
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
3)修改vue.config.ts打包
configureWebpack: {
output: {
// 把子应用打包成 umd 库格式
library: 'live', // 要和主应用定义的名字保持一致
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${name}`,
}
},
以上这样,一个简易的微前端已经完成了
启动本地服务可以看到效果