Appearance
总览思路
切换账户/重连时,除了解绑监听与拒绝挂起请求,还应:
- 取消订阅与动画帧;
- 清除本地缓存快照/脏状态;
- 取消延迟/节流定时器与一次性
watch; - 新连接
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();放置位置:
initWebSocket的onOpenRef中,await ensureSocketAuth()之后且resolve(true)之前。 若你保留了“失败也 resolve”的容错,也可在try{}内调用;失败则仅记录warn。
修改:switchAccount —— 切换前清理 & open 后恢复
ts
// 在切换连接之前做本地清理,防止前后账户数据串台
cleanupBeforeReconnect();放置位置:
switchAccount内,rejectAllPending()之后、解绑旧监听 之后、switchConnection(url)之前。
ts
// open 后先鉴权,再恢复订阅与规格,确保 UI 不“断粮”
await ensureSocketAuth();
// 复杂逻辑:新连接建立后,按当前 UI 恢复订阅并兜底拉取规格
await resubscribeAndWarmupSpecs();放置位置:
switchAccount的onOpenRef中,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。