Files
DCU_DesktopController/docs/compose/spec/tab-keepalive.md
T
cbb eaafee1312 feat(shell): tabs 多 iframe 常驻保活,切标签不再销毁功能页
单 iframe 换 src 会卸载文档,WebSocket/轮询/传输状态全灭。
改为一标签一懒创建 iframe,切换只改 visibility;F5/⟳ 只 reload 激活帧。
2026-09-16 02:37:18 +08:00

3.5 KiB
Raw Blame History

feature, status, updated, branch, commits
feature status updated branch commits
tab-keepalive designed 2026-09-16 feat/tab-keepalive

Tab Keep-Alive(多 iframe 常驻)

Report

[S1] Problem

tabs.html 用单 iframe 承载功能页,切换标签时执行 view.src = path。 这会销毁上一页的整个文档:

  • WebSocket(app-lease、调试通道)断开
  • setInterval / setTimeout 轮询停止
  • 电机页 EtherCAT/WiFi 传输状态、transport-ecat 的 started 标志丢失
  • OTA 页 ecat_load 轮询、SPI 状态轮询冷启动
  • 用户「调完一切、切走再切回」后连接全灭,表现为界面假死/全 --

外壳自身的 app-lease.js 仍在,但功能页内的连接与状态无法幸存。 这是结构性缺陷,不是某个页面的 bug。

[S2] Design

外壳:一标签一 iframe,只 show/hide

  • TABS 每项对应一个懒创建 iframe(class="page-frame",data-page)。
  • 首次进入某标签才 src=page;之后切换只改可见性,不改 src、不 remove。
  • 宿主容器 id="viewHost"(保留 data-shell 语义:注入脚本用 #viewHost 判定外壳)。
  • 激活帧:visibility:visible; pointer-events:auto;非激活:visibility:hidden; pointer-events:none。 不用 display:none——部分引擎对 display:none iframe 的定时器/绘制更狠;visibility + 绝对定位铺满即可。
  • 切换时同步 localStorage.dcu_tab(沿用现有键与 pageToTab 迁移)。

状态保全契约

状态 保活后
功能页内 WS / 轮询 / Module 全局 留在各自 iframe 文档内,切走不断
外壳 app-lease 仍由 tabs.html 自持(覆盖整个窗口)
ecat_native 后台 PDO 循环 服务端 + 租约决定,与是否隐藏无关
F5 / ⟳ 只 reload 激活 iframe,不重载外壳(否则全部保活帧被销毁)

注入刷新脚本(esp32_server.py _RELOAD_SNIPPET)

  • reloadCur():优先 document.querySelector('.page-frame.active');无则退化 location.reload()。
  • 外壳判定:#viewHost 存在(替代原 #view iframe 的语义)。
  • 功能页内(非 TOP)行为不变:去掉悬浮钮,覆写 _blank。

导航与高亮

  • 点击标签 → go(page):确保帧存在 → 激活目标 → 其它帧隐藏。
  • iframe load:若 pathname 映射到某 TAB,更新外壳高亮与 dcu_tab; 不因非激活帧的延迟 load 误抢激活(仅当该帧已是 active 才写 dcu_tab)。
  • 未知页(页内跳到未登记 html):仍留在当前 iframe 内显示,外壳不高亮新标签。

内存

用户裁定「全部标签常驻」。6 个功能页同时活着的代价可接受; 不引入 LRU 卸载(卸载即回到现在的病)。

[S3] Out of Scope

  • 不合并成 SPA、不把连接迁到外壳进程
  • 不改各页内部轮询/传输实现
  • 不改服务端租约协议
  • 不做「后台页降频」策略(浏览器 Page Visibility 自带节流即可)

Tasks

  • T1: tabs.html 多 iframe 宿主 + 懒创建 + show/hide 切换 — acceptance: 切走再切回,OTA/电机页的轮询与 WS 仍在,无需重新连接 (covers: S2)
  • T2: esp32_server.py 注入的 F5/⟳ 只刷新激活帧 — acceptance: 外壳按 F5 仅重载当前标签文档,其它标签 iframe 仍在 DOM 且未 reload (covers: S2)
  • T3: 语法/手工验收路径 — acceptance: node --check 若适用;人工路径:开 OTA→电机→切回 OTA,指标不全灭 (covers: S2)