最近因为两个后端系统要复用相同的功能模块,为了不直接复制代码,避免代码迁移的烦琐以及后期维护巨大的工作量,对两个系统进行了微前端的改造,也为后面vue2升级vue3,融合react框架做铺垫。技术选型采用了阿里的qiankun2.0作为基础框架。
在项目改造中,遇到的一个比较大的问题,就是子应用A弹窗嵌套后样式丢失的问题。
期望是这样的: 实际是这样的:
子应用A,里面有一个嵌套弹窗,这个弹窗是element的dialog组件,因为嵌套弹窗有层级问题,所以里面的弹窗设置了append-to-body,设置完之后,弹窗脱离了子应用,直接加载到主应用的body上了。为了解决这个问题,查阅了很多资料,按照官网的教程配置,都没能成功的解决。后来,受这篇博客的启发(www.modb.pro/db/145390) 自己重写了document.body.appendChild方法。
let instance = null;
let originFn = document.body.appendChild.bind(document.body)
function render(props = {}) {
const { container, router, clientType } = props;
// 改变子应用的弹窗popup的位置
redirectPopup(container)
// 订阅跳转主应用登录页的事件
PubSub.subscribe('to-login', () => {
router.push('/login')
})
//
store.commit('story/SET_CLIENT_TYPE', clientType)
Vue.prototype.$clientType = clientType
instance = new Vue({
store,
render: (h) => h(App),
beforeCreate() {
// 安装全局事件总线
Vue.prototype.$bus = this
},
}).$mount(container ? container.querySelector('#app') : '#app');
}
复制代码
function redirectPopup(container) {
// 子应用中需要挂载到子应用的弹窗的className,用作标记
const addPopup = 'el-dialog__wrapper story-para-add-popup'
const editPopup = 'el-dialog__wrapper story-para-edit-popup'
const whiteList = [addPopup, editPopup]
// 保存原有document.body.appendChild方法
let originFn = document.body.appendChild.bind(document.body)
// 重写appendChild方法
document.body.appendChild = (dom) => {
// 根据标记,来区分是否用新的挂载方式
if(whiteList.includes(dom.className)){
container.querySelector('#app').appendChild(dom)
}else{
originFn(dom)
}
}
}
复制代码
// 为保险起见,最后在子应用注销时,还原document.body.appendChild方法
export async function unmount() {
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
document.body.appendChild = originFn
}
复制代码
之所以要标记使用和还原document.body.appendChild方法,是因为主应用和子应用中有很多组件都用到了这个方法,比如select,日历组件。不还原这个方法的话,这些组件的样式会受到破坏。
本文介绍了一种简单粗暴但有效的解决微前端架构中子应用弹窗样式丢失的方法。如果各位大佬有更优雅的方法,请不吝赐教~