Closed PickACatName closed 3 years ago
https://qiankun.umijs.org/zh/api api 里面对样式基于 ShadowDOM 的严格样式隔离有说明
首先说下为什么不生效: 你说的 dialog 组件 DOM 是挂载在主应用的 document.body 但是样式加载在子应用的 ShadowDOM 里面,ShadowDOM 里的样式不会在主应用中生效
如何解决: 你需要看下组件能否挂载在子应用的 DOM 元素上,不要挂载在 document.body
vant 的 toast 有类似能力
import { Toast } from 'vant';
Vue.use(Toast);
Toast.setDefaultOptions({ getContainer: () => container}); // 挂载在子应用的 container 上
好的,感谢,已解决
好的,感谢,已解决
你的解决方案是什么,element-ui 的?
好的,感谢,已解决
你的解决方案是什么,element-ui 的?
弹窗抽屉之类的组件使用了v-transfer-dom指令挂载到子应用的DOM元素上,像el-popover之类的组件目前暂时用了行内样式
解决思路:ShadowDOM + popup 类组件挂载到子应用某个节点里 解决步骤: 1、主应用 vue2,开启 ShadowDOM 沙箱
start({
sandbox: {
strictStyleIsolation: true,
}
})
2、子应用 public/index.html,增加容器节点
<div id="microAppPopup"></div>
3、子应用 vue3,入口暴露生命周期获取子应用 container
export async function mount(props: any) {
const { container } = props;
const app = await startup(props, container);
if (app) {
app.config.globalProperties.mainAppConfig = props;
}
}
4、App.vue 里查询子应用的 popup 类组件容器节点
<template>
<a-config-provider :locale="locale" :getPopupContainer="getPopupContainer">
<router-view />
</a-config-provider>
</template>
<script>
import { getCurrentInstance } from 'vue';
import zhCN from 'ant-design-vue/es/locale/zh_CN';
export default {
setup() {
const getPopupContainer = (el) => {
const vueActiveInstance = getCurrentInstance();
if (!vueActiveInstance) {
console.log(`未找到 vue 激活实例,使用 document.body`);
return document.body;
}
const { appContext } = vueActiveInstance;
const didaConfig = appContext.app?.config?.globalProperties?.mainAppConfig;
const container = didaConfig?.mainAppConfig?.container;
console.log('getPopupContainer', el, container);
if (typeof container?.querySelector === 'function') {
const wrap = container?.querySelector('#microAppPopup');
console.log('getPopupContainer', wrap);
return wrap;
}
console.log('getPopupContainer 使用 document.body');
return document.body;
};
return {
locale: zhCN,
getPopupContainer,
};
},
};
</script>
点击date-pinker文本框获得焦点,date-picker面板会闪动然后马上消失,不知道什么原因,审查元素看date-picker元素被添加到父应用的body同层,但是即使我代理document.body的appendChild方法到子应用的容器内,还是有这个闪动问题,感觉跟楼主这个问题不是同一个
解决思路:ShadowDOM + popup 类组件挂载到子应用某个节点里 解决步骤: 1、主应用 vue2,开启 ShadowDOM 沙箱
start({ sandbox: { strictStyleIsolation: true, } })
2、子应用 public/index.html,增加容器节点
<div id="microAppPopup"></div>
3、子应用 vue3,入口暴露生命周期获取子应用 container
export async function mount(props: any) { const { container } = props; const app = await startup(props, container); if (app) { app.config.globalProperties.mainAppConfig = props; } }
4、App.vue 里查询子应用的 popup 类组件容器节点
<template> <a-config-provider :locale="locale" :getPopupContainer="getPopupContainer"> <router-view /> </a-config-provider> </template> <script> import { getCurrentInstance } from 'vue'; import zhCN from 'ant-design-vue/es/locale/zh_CN'; export default { setup() { const getPopupContainer = (el) => { const vueActiveInstance = getCurrentInstance(); if (!vueActiveInstance) { console.log(`未找到 vue 激活实例,使用 document.body`); return document.body; } const { appContext } = vueActiveInstance; const didaConfig = appContext.app?.config?.globalProperties?.mainAppConfig; const container = didaConfig?.mainAppConfig?.container; console.log('getPopupContainer', el, container); if (typeof container?.querySelector === 'function') { const wrap = container?.querySelector('#microAppPopup'); console.log('getPopupContainer', wrap); return wrap; } console.log('getPopupContainer 使用 document.body'); return document.body; }; return { locale: zhCN, getPopupContainer, }; }, }; </script>
element-ui的 应该怎么解决呢
vue2版本,element-ui,我暂时通过以下方式解决了,应该也适用于其他UI组件库
const proxy = ({ container }) => {
if (document.body.appendChild.__isProxy__) return
revocable = Proxy.revocable(document.body.appendChild, {
apply(target, thisArg, [node]) {
if (container) {
container.appendChild(node)
} else {
target.call(thisArg, node)
}
}
})
document.body.appendChild = revocable.proxy
document.body.appendChild.__isProxy__ = true
}
export const mount = async props => {
proxy(props)
...
}
export const unmount = async () => {
...
revocable.revoke()
revocable = null
}
vue2版本,element-ui,我暂时通过以下方式解决了,应该也适用于其他UI组件库
const proxy = ({ container }) => { if (document.body.appendChild.__isProxy__) return revocable = Proxy.revocable(document.body.appendChild, { apply(target, thisArg, [node]) { if (container) { container.appendChild(node) } else { target.call(thisArg, node) } } }) document.body.appendChild = revocable.proxy document.body.appendChild.__isProxy__ = true } export const mount = async props => { proxy(props) ... } export const unmount = async () => { ... revocable.revoke() revocable = null }
这样做后会有报错,请教大佬是为什么呢? @songyazhao
点击其他的路由,再点击回子应用的路由,会报错,页面为空白,重新刷新页面才可以
好的,感谢,已解决
你的解决方案是什么,element-ui 的?
弹窗抽屉之类的组件使用了v-transfer-dom指令挂载到子应用的DOM元素上,像el-popover之类的组件目前暂时用了行内样式
v-transfer-dom指令的代码方便展示下吗
好的,感谢,已解决
你的解决方案是什么,element-ui 的? 要不要尝试我的重写的包 https://github.com/Rahim-Chan/qiankun-rewrite
要不要尝试我的重写的包 https://github.com/Rahim-Chan/qiankun-rewrite
要不要尝试我的重写的包,可以完美解决这个问题 https://github.com/Rahim-Chan/qiankun-rewrite
vue2版本,element-ui,我暂时通过以下方式解决了,应该也适用于其他UI组件库
const proxy = ({ container }) => { if (document.body.appendChild.__isProxy__) return revocable = Proxy.revocable(document.body.appendChild, { apply(target, thisArg, [node]) { if (container) { container.appendChild(node) } else { target.call(thisArg, node) } } }) document.body.appendChild = revocable.proxy document.body.appendChild.__isProxy__ = true } export const mount = async props => { proxy(props) ... } export const unmount = async () => { ... revocable.revoke() revocable = null }
你好,这个方式重写body.appendChild方法会把主应用和其它子应用(同时激活多个子应用)的也重写了,导致主应用的弹窗加到了这个子应用里。
vue2版本,element-ui,我暂时通过以下方式解决了,应该也适用于其他UI组件库
const proxy = ({ container }) => { if (document.body.appendChild.__isProxy__) return revocable = Proxy.revocable(document.body.appendChild, { apply(target, thisArg, [node]) { if (container) { container.appendChild(node) } else { target.call(thisArg, node) } } }) document.body.appendChild = revocable.proxy document.body.appendChild.__isProxy__ = true } export const mount = async props => { proxy(props) ... } export const unmount = async () => { ... revocable.revoke() revocable = null }
你好,这个方式重写body.appendChild方法会把主应用和其它子应用(同时激活多个子应用)的也重写了,导致主应用的弹窗加到了这个子应用里。
你解决了嘛,大佬
关于样式隔离的疑问——开启了沙箱后,当子应用中使用了类似element的dialog,drawer等挂在document.body上的组件时,子应用中组件内嵌页面的样式无法生效,有什么好的解决方案么?