本章1~6主要是解决无界微前端环境下element-ui弹框偏移问题,如果你用的是其他微前端框架,且提供了jsloader这种预处理器,则可以举一反三解决同样的问题。
7后面的内容可以通用react、vue、anglar,大家都可以尝试尝试,建议先用插件形式试试看,因为插件可以解决所有UI框架的偏移问题
如果不想看我废话,请直接移步到5看代码和后面的效果图【此方法已经废弃,因为官网更新了源码。现在直接看6.新方法测试可以不偏移】。
1. 首先,我使用的是无界官方源码,下载地址:无界微前端源码
如图已经下载到本地了:使用pnpm i安装一下依赖
如果报错,请更新你的nvm或者使用16.19.0版本的node
2. 启动官网例子:npm run start,正确启动的话可以看到一下页面:
点击进入vue2的dialog页面。
3. 我们打开examples\vue2\src\main.js,在顶部任意地方加入:
import Row from "element-ui/lib/row";
import Col from "element-ui/lib/col";
import "element-ui/lib/theme-chalk/row.css";
import "element-ui/lib/theme-chalk/col.css";
[Row, Col].forEach((element) => Vue.use(element));如图:
4. 打开examples\vue2\src\views\Dialog.vue,写入代码:
<template>
<a-button @click="fullDialogVisible = true" style="margin-left: 20px">点击打开全屏弹窗</a-button>
<el-dialog title="全屏弹窗" fullscreen :visible.sync="fullDialogVisible" width="30%">
<el-row type="flex" justify="space-between">
<el-col :span="6"
><div class="grid-left">
<el-select v-model="value" placeholder="el-select">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select></div
></el-col>
<el-col :span="6"
><div class="grid-center">
<el-select v-model="value" placeholder="el-select">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select></div
></el-col>
<el-col :span="6"
><div class="grid-right">
<el-select v-model="value" placeholder="el-select">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select></div
></el-col>
</el-row>
<span slot="footer" class="dialog-footer">
<el-button @click="fullDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="fullDialogVisible = false">确 定</el-button>
</span>
</el-dialog>
</template>
<script>
...
data() {
return {
fullDialogVisible: false
}
}
...
</script>以上代码就是为了写一个弹框,且弹框内有左中右三个下拉框,来显示下拉框是否位置正常。
5. 【全文重点】 打开examples\main-vue\src\views\Vue2-sub.vue此文件,写入:
<template>
<WujieVue width="100%" height="100%" name="vue2" :url="vue2Url" :plugins="plugins"></WujieVue>
</template>
<script>
...
data() {
return {
plugins: [
{
// 在子应用所有的css之前
cssBeforeLoaders: [
// 强制使子应用body定位是relative
{ content: "body{position: relative !important}" },
],
},
{
jsLoader: (code) => {
// 替换popper.js内计算偏左侧偏移量
var codes = code.replace(
"left: elementRect.left - parentRect.left",
"left: fixed ? elementRect.left : elementRect.left - parentRect.left"
);
// 替换popper.js内右侧偏移量
return codes.replace("popper.right > data.boundaries.right", "false");
},
},
],
}
}
...
</script>按以上操作则可以实现官网例子内的弹框不在偏移。且不论下拉框是何种定位都能实现完美位置。
综上所述:
你只需更改主应用的plugins即可修复弹框偏移问题;按照5所述,修改即可。(费了大量的时间和精力,一直在寻找一个完美且傻瓜式的解决办法,最终还是调试源码,找到此办法。github上解决此问题的人都是各种奇淫技巧,但我们只需要最朴素且简单见效的办法。)
6. 上面提到的方法跟element-ui版本有关,可以尝试一下。如果不行可以采用下面方法试试(只写了改源码。):
解决思路:把popper的dom直接塞进主应用的body中,判断定位和显示边界也直接以主应用作为左边起点,这样的效果就和正常在项目中使用appendToBody的效果一致,代码改动也非常的少。
因为公司内部使用的组件库是基于element-ui进行封装的,所以直接在源码上进行更改了。
如果不能直接修改element-ui的源码,明确修改代码位置后,可以使用js-loader或者项目build之前修改node_modules代码。
以下代码目录都是基于element-ui源码的目录,对应在node_modules中的地址是lib/utils。
找到文件:node_modules/element-ui/src/utils/vue-popper.js
// 源码
this.appendToBody && document.body.appendChild(this.popperElm);
// 修改后
if (this.appendToBody) {
if (window.__POWERED_BY_WUJIE__) {
window.parent.document.body.appendChild(this.popperElm);
} else {
document.body.appendChild(this.popperElm);
}
}
找到文件:node_modules/element-ui/src/utils/popper.js
// 源码
var root = window;
// 修改后
var root = window.__POWERED_BY_WUJIE__ ? window.parent : window;
// 源码
function getStyleComputedProperty(element, property) {
// NOTE: 1 DOM access here
var css = root.getComputedStyle(element, null);
return css[property];
}
// 修改后
function getStyleComputedProperty(element, property) {
// NOTE: 1 DOM access here
// wujie环境下向上遍历offsetParent时 过滤document类型得节点,避免方法getComputedStyle报错
if (window.__POWERED_BY_WUJIE__ && element.nodeType === 9) return 'static';
var css = root.getComputedStyle(element, null);
return css[property];
}jsloader方式
{
jsLoader(code) {
// 解决element-ui,popper.js计算问题
let newCode = code.replace('var root = window;','var root = window.parent')
.replace('document.body.appendChild(this.popperElm);','window.parent.document.body.appendChild(this.popperElm);')
.replace("var css = root.getComputedStyle(element, null);","if (window.__POWERED_BY_WUJIE__ && element.nodeType === 9) return 'static';var css = root.getComputedStyle(element, null);");
return newCode;
}
}最终实现效果展示:
如果替换不成功可以看评论区,把element-ui给排除压缩混淆,再打包,就不会出现替换不到的情况了。或者你也可以通过patch-package给npm包打补丁解决此问题。
7. 其他解决办法。
当然了还有其他同学解决问题的办法。主要是框选内容。这里贴图:
还有甚者通过实现插件形式实现(推荐使用,因为可以解决所有框架偏移问题):
// file:app/wujie/plugins/index.js
export const appendOrInsertElementHook = function (element, iframeWindow) {
if (element.offsetParent && element.offsetParent.tagName !== 'BODY') {
return
}
const offsetParentDesc = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetParent')
Object.defineProperties(element, {
offsetParent: {
configurable: true,
get: function () {
const offsetParent = offsetParentDesc.get.call(this)
if (
(offsetParent && offsetParent.tagName !== 'BODY') ||
element.style.position !== 'fixed'
) {
return offsetParent
}
return new Proxy(window.document.documentElement, {
get: (target, propKey) => {
if (propKey === 'parentNode') {
return iframeWindow.document.documentElement.parentNode
}
const value = target[propKey]
const naughtySafari =
typeof document.all === 'function' && typeof document.all === 'undefined'
// 只有这些场景下才需要 bind
if (
(naughtySafari
? typeof value === 'function' && typeof value !== 'undefined'
: typeof value === 'function') &&
!(value.name.indexOf('bound ') === 0 && !value.hasOwnProperty('prototype'))
) {
const boundValue = Function.prototype.bind.call(value, target)
for (const key in value) {
boundValue[key] = value[key]
}
if (value.hasOwnProperty('prototype') && !boundValue.hasOwnProperty('prototype')) {
Object.defineProperty(boundValue, 'prototype', {
value: value.prototype,
enumerable: false,
writable: true,
})
}
return boundValue
}
return value
},
})
},
},
})
}用法:
react的antd框架弹框偏移。请看我这个项目:
https://gitee.com/quqingfei/wujie-react-demo
// App.css增加这行
.ant-pro-layout .ant-pro-layout-content {
position: initial !important;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用。你还可以使用@来通知其他用户。