1008923 - 切换左侧导航菜单时工作区 PDF 等文档出现闪烁
缺陷信息
- 缺陷 ID:1008923
- 标题:当前切换左侧导航菜单时,工作区已打开的 PDF 等文档会出现闪烁现象
- 优先级:high
- 负责人:尹帮会
- 创建者:尹帮会
- 创建时间:2026-03-19
问题描述
在工作区打开 PDF、Word 等文档后,切换左侧导航菜单(主页 / 工作台 / 知识库)再切回工作台,文档会出现短暂闪烁或白屏。
根因分析
MainLayout.vue 使用 v-show 切换三个常驻页面(WelcomePage / WorkspacePage / KnowledgeBasePage)。
v-show 的底层实现是 display: none / block,切换时会:
- 将 WorkspacePage 从布局流中移除(
display: none) - 恢复时触发整个组件树的 DOM 重排(reflow)
- 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确保隐藏页面不响应用户交互 - 同时将
.workspace的transition: 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: 0。opacity 与 visibility 不同,子元素无法超过父元素的 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-activeclass 切换生效,激活页opacity:1,非激活页opacity:0