qiankun 和 Element UI 影响 el-cascader 宽度问题

发布于:2025-07-24 ⋅ 阅读:(23) ⋅ 点赞:(0)

问题现象

在这里插入图片描述

问题的根源:一切始于 zoom

  1. 主应用的全局缩放:在主应用的 mounted 钩子中,有一段核心代码 document.getElementById('main').style.zoom = scaleW;。为了让页面能自适应不同分辨率的屏幕,主应用对整个容器(包括未来所有子应用)应用了一个 zoom 缩放。例如,当 scaleW0.75 时,整个页面都被浏览器缩小到了 75%。
  2. JavaScript 的“视觉欺骗”zoom 是一个比较特殊的 CSS 属性。当用 JavaScript 去测量一个被缩放的元素的宽度时(例如 element.offsetWidth),得到的是它在屏幕上看起来的宽度(视觉宽度),而不是它在 CSS 中定义的原始宽度。

为什么会“越点越小”?—— 灾难性的“双重缩放”

当点击子应用中的 el-cascader 时,一场混乱的计算就开始了:

  1. 第一次错误计算el-cascader 的下拉框(由 Popper.js 库控制)需要知道它应该有多宽。于是它去测量输入框的宽度。假设输入框原始宽度是 200px,因为被主应用 zoom: 0.75 了,Popper.js 测量到的结果是 150px
  2. 第一次错误应用:Popper.js 拿到这个 150px 的值,就把它设置给了下拉框的 style="width: 150px;"
  3. 第二次灾难性缩放:Element UI 出于某种原因,又给下拉框内部的面板 .el-cascader-panel 也加上了 style="zoom: 0.75;"
  4. 恶性循环:这就导致了“双重缩放”。下拉框容器的宽度被错误地设为了 150px,而它里面的内容又被再次缩放了 75%。这导致布局彻底混乱。当再次点击时,下一次计算又基于这个已经混乱的布局,得到一个更小的值,如此往复,就出现了“越点越小”的现象。

解决方案的原理:在主应用中“反向解题”

对抗来自主应用的全局 zoom

最终的解决方案是在主应用里,通过提供的那个全局函数 window.computedPopperPosition,从根源上修正这个计算过程。它的原理就像“反向解题”:

  1. 获取缩放比例:函数首先获取到主应用当前设置的全局 zoomscaleW(例如 0.75)。
  2. 获取错误结果:它拿到 Popper.js 测量出的那个被缩小的视觉宽度 visualWidth(例如 150px)。
  3. 反向计算出正确答案:这是最关键的一步。通过公式 真实宽度 = 视觉宽度 / 缩放比例realWidth = visualWidth / scaleW),它用 150 / 0.75 计算出了输入框在缩放前真正的宽度 200px
  4. 强行修正
    • 它把这个正确的 200px 强行设置给下拉框的 style.width,覆盖掉 Popper.js 的错误计算结果。
    • 同时,它找到下拉框内部那个被 Element UI 再次缩放的面板 .el-cascader-panel,并强行将它的 zoom 设回 1彻底打破了“双重缩放”的循环
qiankun主应用代码
window.computedPopperPosition = function (dataParam) {
      // 1. 计算主应用设置的 zoom 缩放比例
      const scaleW = Number(parseFloat(Math.max(window.innerWidth, 1366) / 1920).toFixed(2));

      // 如果没有缩放,则直接返回,不做任何处理
      if (scaleW >= 1) {
        return dataParam;
      }

      const instance = dataParam.instance;
      // 确保 Popper 实例和相关 DOM 元素都存在
      if (instance && instance._reference && instance._popper) {
        const referenceEl = instance._reference; // 这是被缩放的输入框
        const popperEl = instance._popper;     // 这是未被缩放的下拉框

        // 确认下拉框是挂载到 body 下的,这是 el-cascader 的默认行为
        const appendToBody = popperEl.getAttribute('appendToBody');
        if (appendToBody === 'true' || appendToBody === true) {

          // 2. 测量输入框在缩放后的视觉宽度
          const visualWidth = referenceEl.offsetWidth;

          // 3. 计算出输入框在缩放前的“真实”宽度
          const realWidth = visualWidth / scaleW;

          // 4. 将这个“真实”宽度强行赋值给下拉框的 style.width
          popperEl.style.width = `${realWidth}px`;

          // 5. 【关键】强制将下拉框自身的 zoom 设置为 1,防止双重缩放
          popperEl.style.zoom = 1;

          // 遍历并重置子元素的 zoom,确保内容也恢复正常
          for (let i = 0; i < popperEl.children.length; i++) {
            if (popperEl.children[i].style) {
              popperEl.children[i].style.zoom = 1;
            }
          }
        }
      }

      return dataParam;
    }

网站公告

今日签到

点亮在社区的每一天
去签到