浅入浅出,微前端qiankun
本文主要是记录在公司使用 qiankun 遇到的问题,和使用的心得,不是教程
在说qiankun前,先简单介绍下微前端
- 微前端是什么
- 由不同独立发布的系统共同构建web的方法
- 为什么情况下用微前端
- 项目需要独立部署,并被其他系统集成
- 长期大项目,需要不断迭代
- 为什么选择qiankun
- 国内使用的人多,相比较 single-spa,开箱即用
- 美中不足
- 原生不支持vite
- 缺少较好的组件共享方案
- 和 MicroApp 比较,接入qiankun的改动会更多
如何使用qiankun
基座的改动 (只有基座需要引入qiankun)
yarn add qiankun
//src/index 入口文件
import { registerMicroApps, start } from 'qiankun';
export const app: RegistrableApp<any>[] = [
{
name: 'micro', //微应用名称
entry:`http://localhost:9000/`, // 微应用地址
container: '#container', // 基座会在这个DOM下渲染微应用
activeRule: '/micro', //触发路径,需要和微应用router的basename相同
props: { store }, // 把基座的redux传递给微应用,微应用就可以使用如token、userinfo等数据
},
];
// 注册微应用
registerMicroApps(app);
// 渲染微应用
start()
React微应用的改动主要有4个点
- public地址
微应用修改__webpack_public_path__(webpack动态public)。不修改这个,主应用访问微应用时,资源路径会有问题
src目录下创建public-path.js
// src/public-path.js
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
在入口index文件中引入
// src/index.tsx
import './public-path'; //一定要在入口文件顶部引入
- 入口文件生命周期
微应用启动生命周期,主应用触发url后调用对应的微应用
// src/index.tsx
// activeRule 为注册微应用时候的地址
// 根据qiankun环境变量,单独运行微应用
if (!window.__POWERED_BY_QIANKUN__) {
render({});
}
// 仅首次匹配activeRule执行,在这里执行render
export async function bootstrap() {}
// 每次匹配activeRule就会执行
export async function mount(props) {
const parentDva = props?.store;
if (parentDva) {
// 这里拿到的基座的store,里面有的登录后的token,用户信息等数据
render(props);
}
}
// 每次离开activeRule就会卸载
export async function unmount() {
const { container } = props;
ReactDOM.unmountComponentAtNode(
container
? container.querySelector('#root')
: document.querySelector('#root'),
}
- Router的basename属性 修改子应用basename
// 根据qiankun环境变量决定pathname
<Router basename={window.__POWERED_BY_QIANKUN__ ? '/micro' : '/'}>
<Routers />
</Router>
- 开启webpack打包跨域,修改打包后的格式为UMD
开启跨域,更改成umd格式
// config-overrides.js
(config) => {
// 修改打包格式为UMD
config.output.library = `${packageName}-[name]`;
config.output.libraryTarget = 'umd';
config.output.chunkLoadingGlobal = `webpackJsonp_${packageName}`;
config.output.globalObject = 'window';
return config;
}
devServer: overrideDevServer((config) => {
// 修改CORS开启跨域,让基座能通过fetch加载微应用资源
config.headers = config.headers || {};
config.headers['Access-Control-Allow-Origin'] = '*';
return config;
}, watchAll()),
改造完成
qiankun Options配置
sandbox样式隔离
- strictStyleIsolation 通过Shadow DOM来隔离,3.0计划删除,会隔离样式和事件,引出更多的问题
- experimentalStyleIsolation
命名空间,给微应用增加类名前缀,但主应用对微应用的样式影响依旧存在
-
主应用样式依旧会影响微应用
-
微应用一些挂在body的组件样式会失效,比如antd的modal,Select,但是可以在ConfigProvider配置挂载DOM
-
// App.tsx
ConfigProvider.config({
prefixCls: 'base-ant',
});
// config-overrides.js
addLessLoader({
lessLoaderOptions: {
lessOptions: {
modifyVars: {
'ant-prefix': 'base-ant',
},
javascriptEnabled: true,
},
},
}),
JS沙箱 (保持微应用window的隔离)
-
snapshotSandbox
只能一个微应用,因为要遍历window的属性所以性能不好
-
proxySandbox
支持多微应用,需要proxy
-
legacySandbox(3.0计划删除)
只能一个微应用,需要proxy,但是性能比snapshotSandbox更好
遇到的问题
微应用主应用通信
主应用传递dva-core给微应用,从store拿数据,比如登录后的token和userinfo
微应用发版后缓存
微应用的http缓存,微应用发版之后,chunk文件会变化,但是index.html是没有的,所以会加载改变之前的chunk文件
我的做法是nginx给微应用的index.html加上了Cache-Control: no-store,no-Cache,这样js、css还是能够缓存,也避免了chunk加载错误的问题
路由跳转
微应用A想跳转到微应用B,用自己的router跳转,会带上basename
- 主应用传递router给微应用,再用这个router跳转
- 使用history,或者history.js跳转
获取微应用的信息
场景是我需要在加载微应用前就获取到微应用的路由信息,才能提前生成菜单
因为微应用已经允许跨域,这里我采用的是基座直接fetch微应用public下的json文件
// url是微应用的地址
fetch(`${url}/routerConfig.json`)
.then(async (data) => {
const json = await data.json();
if (json) {
// 拿到了json根据json去生成菜单,这里代码省略
}
})
微应用部署时怎么部署到一个端口
就我希望基座/base 微应用/base/child/micro github.com/umijs/qiank… 这里附上 gongshun大佬给的解决方案
转载自:https://juejin.cn/post/7126399393367851016