Skip to content

总览思路

切换账户/重连时,除了解绑监听与拒绝挂起请求,还应:

  1. 取消订阅与动画帧;
  2. 清除本地缓存快照/脏状态;
  3. 取消延迟/节流定时器与一次性 watch
  4. 新连接 open 后恢复订阅并“兜底”拉取所选合约规格(因为 selectedTab 未变化不会触发已有的 watch)。

下面只给需要新增/修改的代码片段(遵循你的偏好),复杂逻辑均在上一行注释说明。

新增:统一清理函数(切换前/重连前调用)

ts
// 账户切换/重连前的本地清理,避免脏数据与内存泄漏
function cleanupBeforeReconnect() {
  // 1) 停止订阅与下一帧调度
  unsubscribeAll();
  if (rafId != null) {
    cancelAnimationFrame(rafId);
    rafId = null;
  }
  if (subRafId != null) {
    cancelAnimationFrame(subRafId);
    subRafId = null;
  }

  // 2) 取消节流/延迟与一次性 watch
  if (fetchSpecsTimeout) {
    clearTimeout(fetchSpecsTimeout);
    fetchSpecsTimeout = null;
  }
  if (waitAccountStop) {
    try {
      waitAccountStop();
    } catch {}
    waitAccountStop = null;
  }

  // 3) 清空本地缓存状态,避免旧账户数据“串台”
  lastQuoteMap.clear();
  prevQuoteSnapshot.clear();
  changedSymbols.clear();
  pendingAccountMargin.clear();
  orderOperationEvents.value = [];
  quoteTick.value = 0;
}

新增:新连接建立后的“恢复订阅 + 兜底规格拉取”

ts
// 新连接 open 后,根据当前 UI 状态恢复订阅,并兜底拉取所选合约规格
async function resubscribeAndWarmupSpecs() {
  // 订阅 pinned(selectedTab)与 tabs 中的合约,避免空白期
  const targets = new Set<string>();
  if (selectedTab.value) targets.add(selectedTab.value.symbol);
  tabs.value.forEach((t) => targets.add(t.symbol));
  if (targets.size > 0) {
    subscribe(Array.from(targets), "update");
  }

  // 兜底:selectedTab 未变更时,原有 watch 不会触发,这里强制拉一次规格
  const account = accountStore.selectedAccount;
  if (selectedTab.value) {
    if (account) {
      try {
        await symbolInfoStore.fetchSpecs([selectedTab.value], account);
      } catch (e) {
        console.warn("[trade] resubscribeAndWarmupSpecs fetchSpecs error:", e);
      }
    } else {
      // 账户尚未就绪则延迟一次
      if (!waitAccountStop) {
        const stop = watch(
          () => accountStore.selectedAccount,
          (acc) => {
            if (!acc || !selectedTab.value) return;
            symbolInfoStore
              .fetchSpecs([selectedTab.value], acc)
              .catch((err) =>
                console.warn("[trade] deferred warmup fetchSpecs error:", err)
              )
              .finally(() => {
                stop();
                waitAccountStop = null;
              });
          },
          { immediate: false }
        );
        waitAccountStop = stop;
      }
    }
  }
}

修改:initWebSocket —— 切换前清理 & open 后恢复

ts
// 在创建新连接之前做本地清理,防止旧状态污染
cleanupBeforeReconnect();

放置位置:initWebSocket 内,rejectAllPending() 之后、解绑旧监听 之后switchConnection(url) 之前

ts
// 在 onOpenRef 一次性监听内,ensureSocketAuth 成功后恢复订阅与规格
await ensureSocketAuth();
// 复杂逻辑:新连接建立后,按当前 UI 恢复订阅并兜底拉取规格
await resubscribeAndWarmupSpecs();

放置位置:initWebSocketonOpenRef 中,await ensureSocketAuth() 之后resolve(true) 之前。 若你保留了“失败也 resolve”的容错,也可在 try{} 内调用;失败则仅记录 warn


修改:switchAccount —— 切换前清理 & open 后恢复

ts
// 在切换连接之前做本地清理,防止前后账户数据串台
cleanupBeforeReconnect();

放置位置:switchAccount 内,rejectAllPending() 之后、解绑旧监听 之后switchConnection(url) 之前

ts
// open 后先鉴权,再恢复订阅与规格,确保 UI 不“断粮”
await ensureSocketAuth();
// 复杂逻辑:新连接建立后,按当前 UI 恢复订阅并兜底拉取规格
await resubscribeAndWarmupSpecs();

放置位置:switchAccountonOpenRef 中,await ensureSocketAuth() 之后resolve(true) 之前


可选强化(按需采纳)

ts
// 复杂逻辑:在 handleIncomingData 收到 auth:ok 时,也可触发一次恢复(容错)
// 放在 txt === 'auth:ok' 分支内最后:
// resubscribeAndWarmupSpecs().catch(e => console.warn('[trade] auth-ok warmup fail:', e))

用途:极端情况下 open 发生但 auth:ok 延迟,或 open 内恢复失败时的兜底自愈。


为什么这样做(要点对齐)

  • 资源释放完整:取消订阅、RAF、定时器与一次性 watcher,避免泄漏与重复执行。
  • 状态原子化:清空报价快照/变更集合/账户资金缓冲,避免旧账户残留影响 UI。
  • 用户感知连贯open 后立即恢复订阅并拉取所选合约规格,减少空窗与“静态屏”。
  • 与现有逻辑互补:已有 watch(selectedTab) 在 tab 改变时拉规格;本文补齐“tab 未变但账户变”的兜底路径。

如果你愿意,我可以把上述片段精确插入到你当前文件的对应行并给出带行号的 diff。

本站总访问