微应用开发注意事项
1、window.open 不能滥用,(在微应用开发里,必须使用dispatchWindowOpen)
邻汇吧项目跳转不能用orgin,但是其他三方链接跳转得用orgin!!!
错误示例:
const url = type === 'placeName' ? `/placeMng/detail?tenantPlaceId=${id}` : `/pointMng/detail?tenantSpotId=${id}`;
dispatchWindowOpen(location.origin + url);
正确做法:
dispatchWindowOpen(url);
2、navigate跳转不可滥用,(在微应用开发里优先使用 dispatchNavigate)
3、微应用项目开发,不能直接用 ant的 Modal,如 Modal.confirm 等函数式写法
必须使用已经封装好的window.microModal,如 window.microModal.confirm
4、微应用项目开发,不能直接用 ant的 message,如 message.error, message.info 等函数式写法
必须使用已经封装好的window.microMessage,如 window.microMessage.error
5、微应用项目开发,不能直接用 ant的 notification,如 notification.error, notification.info 等函数式写法
必须使用已经封装好的window.microNotification,如 window.microNotification.error
<Image.PreviewGroup>
组件
6、所有用到 Image组件的预览功能时,如 需要进行如下设置
<PreviewGroup preview={{
getContainer: false
}}>
Anchor
组件,在主应用是hash模式时会有问题
7、不要直接用antd的 请使用经过封装的 CustomAnchor
组件,参数和Anchor一致
.ant-xx
不会生效,需要使用.resant
8、样式覆盖使用9、document的使用需要注释
document是获取的外层的,是不能正确的获取到沙河内部的,当然querySelector可以,但是会优先查找父级,所以需要通过如下方式操作
import { MainAppContext } from '@/index';
import { useContext } from 'react';
const demo = () => {
const { container } = useContext(MainAppContext);
}
在使用到的地方
// container就是微应用沙盒对象
(container || document)?.querySelector('#spotForm .resant-form-item-has-error');
10、当出现套娃弹窗时需要注意
示例:
先弹出一个 drawer,再点击按钮弹出一个modal,同时modal上挂载了forcerender
因为预加载了modal,他会在drawer之前生成浮层,就会导致drawer遮盖住mode
解决方案:
html文件有个
<Modal
...
getContainer={() => (container || document).querySelector('#lhb-mirco-matryoshka')}
11、useModal、Modal.method()用法注意
在微应用内,必须使用useModal写法,不能用函数写法!!! 直接使用window.microModal可能会有层级问题。可以直接使用封装的ConfirmModal,z-index固定1009(仅次于microMessage),如有其他层级混乱可参考第9点套娃弹窗,尽量不要使用z-index修改层级。
// 详细可参考 resource-service-web 的 ConfirmModal
export const ConfirmModal = ({
onSure = () => {},
onCancel = () => {},
cancelText = '取消',
okText = '确定',
icon = <ExclamationCircleOutlined />,
title = '提示',
content = '此操作将永久删除该数据, 是否继续?',
}:ConfirmModalProps) => {
// 在微应用内,必须使用useModal写法,不能用函数写法!!!
const modal = window.microModal.confirm({
title: title,
content: content,
icon: icon,
okText: okText,
cancelText: cancelText,
zIndex: 1009,
onOk: () => onSure(modal),
onCancel: () => onCancel(modal)
});
};
12、CustomAnchor用法注意
在微应用内,必须使用className做锚点定位,使用id会导致在history模式下不起作用
Image.PreviwerGroup
用法注意
13、在微应用内必选在Image.PreviwerGroup
节点上挂载getContainer
否则不会出现顶部控制栏以及左右切换按钮
const { container } = useContext(MainAppContext);
<Image
key={index}
src={QiniuImageUrl(item.url)}
style={{ width, height }}
preview={{ visible: false }}
onClick={() => {
setImageIndex(index);
setPreviewVisible(true);
}}
/>
<div style={{ display: 'none' }}>
<Image.PreviewGroup preview={{
visible: previewVisible,
onVisibleChange: vis => setPreviewVisible(vis),
getContainer: (container || document)?.querySelector('#popContainer'), // 需要挂载到当前应用节点,否则不会出现顶部工具栏以及左右切换按钮
current: imagesIndex
}}>
{ images.map((image, index) => { return <Image src={QiniuImageUrl(image.url)} key={index} />; }) }
</Image.PreviewGroup>
</div>
14、微应用之间如何跳转?
● 主应用和微应用都是 hash 模式,主应用根据 hash 来判断微应用,则不用考虑这个问题。 ● 主应用根据 path 来判断微应用history 模式的微应用之间的跳转,或者微应用跳主应用页面,直接使用微应用的路由实例是不行的,原因是微应用的路由实例跳转都基于路由的 base。
history.pushState(null, '档期管理', window.location.origin + '/schedule');
// 不建议location.href 会有‘白屏现象’
window.location.href = '/schedule';
b. 将主应用的路由实例通过 props 传给微应用,微应用这个路由实例跳转。
15、跳转应用样式丢失问题
主应用vue,子应用react
在子项目跳转到父项目时,子项目的卸载需要一点点的时间,在这段时间内,父项目加载了document.head.appendChild被改写了,导致vue-style-loader没有把样式加到正确的节点上。
解决办法
import VueRouter, { Route } from 'vue-router';
import { Message } from 'element-ui';
const childRoute = ['/lhb-micro-pms']; // 子应用前缀
const isChildRoute = (path: string) => childRoute.some(item => path.startsWith(item));
// 临时解决方案:复制一份HTMLHeadElement.prototype.appendChild和window.addEventListener,路由钩子函数beforeEach中判断一下,如果当前路由是子项目,并且去的路由是父项目的,则还原这两个对象
// const rawAppendChild = HTMLHeadElement.prototype.appendChild;
// const rawAddEventListener = window.addEventListener;
import { setCurrentRunningApp } from 'qiankun/es/sandbox/common';
export default function routerControl(router: VueRouter) {
router.beforeEach((to: Route, from: Route, next: Function) => {
// 判断如果是子应用跳转到父应用
if (isChildRoute(from.path) && !isChildRoute(to.path)) {
// 临时解决方案:
//HTMLHeadElement.prototype.appendChild = rawAppendChild;
//window.addEventListener = rawAddEventListener;
// 建议解决方案:
setCurrentRunningApp(null); // 清空当前应用设置,相当于重新加载
}
// 动态设置页面title
if (to.meta.title) {
document.title = to.meta.title;
} else {
process.env.NODE_ENV === 'development' && Message.error('为了方便数据采集,请添加title!');
}
// 路由为空时重定向回首页
if (to.path === '/') {
router.replace('/schedule');
} else {
next();
}
});
router.afterEach((to: any, from: any) => {
if (process.env.NODE_ENV !== 'development') {
// 先发送离开页面的信息 再上传进入页面的信息
window.LHBbigdata.spa.beforeRouteLeave()(to, from);
window.LHBbigdata.spa.beforeRouteEnter()(to, from);
}
});
}