01-1008923-工作区文档切换闪烁-2轮迭代.md 4.65 KB

1008923 - 切换左侧导航菜单时工作区 PDF 等文档出现闪烁

缺陷信息

  • 缺陷 ID:1008923
  • 标题:当前切换左侧导航菜单时,工作区已打开的 PDF 等文档会出现闪烁现象
  • 优先级:high
  • 负责人:尹帮会
  • 创建者:尹帮会
  • 创建时间:2026-03-19

问题描述

在工作区打开 PDF、Word 等文档后,切换左侧导航菜单(主页 / 工作台 / 知识库)再切回工作台,文档会出现短暂闪烁或白屏。

根因分析

MainLayout.vue 使用 v-show 切换三个常驻页面(WelcomePage / WorkspacePage / KnowledgeBasePage)。

v-show 的底层实现是 display: none / block,切换时会:

  1. 将 WorkspacePage 从布局流中移除(display: none
  2. 恢复时触发整个组件树的 DOM 重排(reflow)
  3. WorkspacePage 内部的 iframe(OnlyOffice、PDF.js)在重排时出现短暂白屏/闪烁

此外,.workspace 上的 transition: all 0.3s ease 会把所有 CSS 属性变化都动画化,加剧了切换时的视觉抖动。

最终修复方案

opacity: 0/1 + position: absolute 替代 v-show

  • opacity: 0 不会将元素移出布局流,iframe 始终保持在 DOM 中
  • 切换时不触发重排,iframe 内容不需要重新渲染
  • pointer-events: none 确保隐藏页面不响应用户交互
  • 同时将 .workspacetransition: all 改为只过渡 flex,避免不必要的动画

注意:第 1 轮尝试用 visibility: hidden 但存在回归问题,第 2 轮改为 opacity: 0 解决。

迭代记录

第 1 轮

修改文件:

  • src/layout/MainLayout.vue

变更内容:

<!-- 修复前:v-show 触发 display:none/block -->
<WorkspacePage
  v-if="wasVisited('Workspace')"
  v-show="route.name === 'Workspace'"
/>

<!-- 修复后:CSS visibility 切换,不移出布局流 -->
<WorkspacePage
  v-if="wasVisited('Workspace')"
  class="page-layer"
  :class="{ 'page-active': route.name === 'Workspace' }"
/>
.workspace {
  position: relative;
  transition: flex 0.3s ease; // 只过渡 flex,避免 all 引发不必要动画
}

.page-layer {
  position: absolute;
  inset: 0;
  visibility: hidden;
  pointer-events: none;

  &.page-active {
    visibility: visible;
    pointer-events: auto;
  }
}

结果: 切换导航菜单时 iframe 不再闪烁,但发现回归问题:切换到 /app/welcome 时,WorkspacePage 内部有子组件显式设置 visibility: visible(如 .tab-content-layer.active),导致隐藏状态被子元素穿透,工作区内容仍然可见。进入第 2 轮。


第 2 轮

修改文件:

  • src/layout/MainLayout.vue

问题根因:

CSS visibility 的继承特性:父元素设置 visibility: hidden 后,子元素可以通过显式设置 visibility: visible 来覆盖,使自己重新可见。这导致 WorkspacePage 内部的激活标签页内容穿透了父层的隐藏效果。

解决方案:

visibility: hidden 改为 opacity: 0opacityvisibility 不同,子元素无法超过父元素的 opacity 值,即父元素 opacity: 0 时,子元素无论如何设置都无法变得可见。

变更内容:

/* 修复前(第 1 轮):visibility 方案,子元素可穿透 */
.page-layer {
  position: absolute;
  inset: 0;
  visibility: hidden;
  pointer-events: none;

  &.page-active {
    visibility: visible;
    pointer-events: auto;
  }
}

/* 修复后(第 2 轮):opacity 方案,子元素无法穿透 */
.page-layer {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;

  &.page-active {
    opacity: 1;
    pointer-events: auto;
  }
}

结果: 切换导航菜单时 iframe 不再闪烁,且切换到 /app/welcome 时工作区内容完全隐藏,回归问题消除。

测试

测试文件: e2e/tests/2026-03-25/1008923-workspace-pdf-flicker.spec.ts

测试覆盖三种隐藏方式的核心差异:

方式 移出布局流 子元素可穿透 结论
display: none ✅ 是 触发重排,iframe 闪烁
visibility: hidden ❌ 否 ✅ 是 不闪烁,但子元素可穿透(第 1 轮回归原因)
opacity: 0 ❌ 否 ❌ 否 不闪烁,不可穿透(最终方案)

验证点:

  • display:none 会将元素移出布局流 → 恢复时触发重排 → iframe 闪烁
  • visibility:hidden 可被子元素用 visibility:visible 穿透 → 导致回归
  • opacity:0 子元素无法超过父元素的 opacity → 既无闪烁又无穿透
  • page-active class 切换生效,激活页 opacity:1,非激活页 opacity:0