refactor: streamline app initialization and enhance WebSocket cleanup logic
This commit is contained in:
@@ -26,15 +26,15 @@ export const AppDataProvider = ({
|
||||
}) => {
|
||||
const { verge } = useVerge();
|
||||
|
||||
// 基础数据 - 中频率更新 (5秒)
|
||||
const { data: proxiesData, mutate: refreshProxy } = useSWR(
|
||||
"getProxies",
|
||||
calcuProxies,
|
||||
{
|
||||
refreshInterval: 5000,
|
||||
revalidateOnFocus: true,
|
||||
refreshInterval: 8000,
|
||||
revalidateOnFocus: false,
|
||||
suspense: false,
|
||||
errorRetryCount: 3,
|
||||
errorRetryCount: 2,
|
||||
dedupingInterval: 3000,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -42,23 +42,23 @@ export const AppDataProvider = ({
|
||||
"getClashConfig",
|
||||
getBaseConfig,
|
||||
{
|
||||
refreshInterval: 60000, // 60秒刷新间隔,减少频繁请求
|
||||
refreshInterval: 60000,
|
||||
revalidateOnFocus: false,
|
||||
suspense: false,
|
||||
errorRetryCount: 3,
|
||||
errorRetryCount: 2,
|
||||
dedupingInterval: 5000,
|
||||
},
|
||||
);
|
||||
|
||||
// 提供者数据
|
||||
const { data: proxyProviders, mutate: refreshProxyProviders } = useSWR(
|
||||
"getProxyProviders",
|
||||
calcuProxyProviders,
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
dedupingInterval: 3000,
|
||||
dedupingInterval: 5000,
|
||||
suspense: false,
|
||||
errorRetryCount: 3,
|
||||
errorRetryCount: 2,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -68,26 +68,26 @@ export const AppDataProvider = ({
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
suspense: false,
|
||||
errorRetryCount: 3,
|
||||
errorRetryCount: 2,
|
||||
dedupingInterval: 5000,
|
||||
},
|
||||
);
|
||||
|
||||
// 低频率更新数据
|
||||
const { data: rulesData, mutate: refreshRules } = useSWR(
|
||||
"getRules",
|
||||
getRules,
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
suspense: false,
|
||||
errorRetryCount: 3,
|
||||
errorRetryCount: 2,
|
||||
dedupingInterval: 5000,
|
||||
},
|
||||
);
|
||||
|
||||
// 监听profile和clash配置变更事件
|
||||
useEffect(() => {
|
||||
let lastProfileId: string | null = null;
|
||||
let lastUpdateTime = 0;
|
||||
const refreshThrottle = 500;
|
||||
const refreshThrottle = 800;
|
||||
|
||||
let isUnmounted = false;
|
||||
const scheduledTimeouts = new Set<number>();
|
||||
@@ -95,14 +95,17 @@ export const AppDataProvider = ({
|
||||
|
||||
const registerCleanup = (fn: () => void) => {
|
||||
if (isUnmounted) {
|
||||
fn();
|
||||
try {
|
||||
fn();
|
||||
} catch (error) {
|
||||
console.error("[数据提供者] 立即清理失败:", error);
|
||||
}
|
||||
} else {
|
||||
cleanupFns.push(fn);
|
||||
}
|
||||
};
|
||||
|
||||
const addWindowListener = (eventName: string, handler: EventListener) => {
|
||||
// eslint-disable-next-line @eslint-react/web-api/no-leaked-event-listener -- cleanup is returned by this helper
|
||||
window.addEventListener(eventName, handler);
|
||||
return () => window.removeEventListener(eventName, handler);
|
||||
};
|
||||
@@ -111,9 +114,13 @@ export const AppDataProvider = ({
|
||||
callback: () => void | Promise<void>,
|
||||
delay: number,
|
||||
) => {
|
||||
if (isUnmounted) return -1;
|
||||
|
||||
const timeoutId = window.setTimeout(() => {
|
||||
scheduledTimeouts.delete(timeoutId);
|
||||
void callback();
|
||||
if (!isUnmounted) {
|
||||
void callback();
|
||||
}
|
||||
}, delay);
|
||||
|
||||
scheduledTimeouts.add(timeoutId);
|
||||
@@ -129,66 +136,48 @@ export const AppDataProvider = ({
|
||||
const newProfileId = event.payload;
|
||||
const now = Date.now();
|
||||
|
||||
console.log(`[AppDataProvider] Profile切换事件: ${newProfileId}`);
|
||||
|
||||
if (
|
||||
lastProfileId === newProfileId &&
|
||||
now - lastUpdateTime < refreshThrottle
|
||||
) {
|
||||
console.log("[AppDataProvider] 重复事件被防抖,跳过");
|
||||
return;
|
||||
}
|
||||
|
||||
lastProfileId = newProfileId;
|
||||
lastUpdateTime = now;
|
||||
|
||||
// 刷新规则数据
|
||||
refreshRules().catch((error) =>
|
||||
console.warn("[AppDataProvider] 规则刷新失败:", error),
|
||||
);
|
||||
refreshRuleProviders().catch((error) =>
|
||||
console.warn("[AppDataProvider] 规则提供者刷新失败:", error),
|
||||
);
|
||||
scheduleTimeout(() => {
|
||||
refreshRules().catch((error) =>
|
||||
console.warn("[数据提供者] 规则刷新失败:", error),
|
||||
);
|
||||
refreshRuleProviders().catch((error) =>
|
||||
console.warn("[数据提供者] 规则提供者刷新失败:", error),
|
||||
);
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const handleRefreshClash = () => {
|
||||
const now = Date.now();
|
||||
console.log("[AppDataProvider] Clash配置刷新事件");
|
||||
|
||||
if (now - lastUpdateTime <= refreshThrottle) {
|
||||
return;
|
||||
}
|
||||
if (now - lastUpdateTime <= refreshThrottle) return;
|
||||
|
||||
lastUpdateTime = now;
|
||||
|
||||
scheduleTimeout(async () => {
|
||||
try {
|
||||
console.log("[AppDataProvider] Clash刷新 - 刷新代理数据");
|
||||
await refreshProxy();
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"[AppDataProvider] Clash刷新时刷新代理数据失败:",
|
||||
error,
|
||||
);
|
||||
}
|
||||
}, 0);
|
||||
scheduleTimeout(() => {
|
||||
refreshProxy().catch((error) =>
|
||||
console.error("[数据提供者] 代理刷新失败:", error),
|
||||
);
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const handleRefreshProxy = () => {
|
||||
const now = Date.now();
|
||||
console.log("[AppDataProvider] 代理配置刷新事件");
|
||||
|
||||
if (now - lastUpdateTime <= refreshThrottle) {
|
||||
return;
|
||||
}
|
||||
if (now - lastUpdateTime <= refreshThrottle) return;
|
||||
|
||||
lastUpdateTime = now;
|
||||
|
||||
scheduleTimeout(() => {
|
||||
refreshProxy().catch((error) =>
|
||||
console.warn("[AppDataProvider] 代理刷新失败:", error),
|
||||
console.warn("[数据提供者] 代理刷新失败:", error),
|
||||
);
|
||||
}, 100);
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const initializeListeners = async () => {
|
||||
@@ -235,7 +224,24 @@ export const AppDataProvider = ({
|
||||
return () => {
|
||||
isUnmounted = true;
|
||||
clearAllTimeouts();
|
||||
cleanupFns.splice(0).forEach((fn) => fn());
|
||||
|
||||
const errors: Error[] = [];
|
||||
cleanupFns.splice(0).forEach((fn) => {
|
||||
try {
|
||||
fn();
|
||||
} catch (error) {
|
||||
errors.push(
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.error(
|
||||
`[数据提供者] 清理过程中发生 ${errors.length} 个错误:`,
|
||||
errors,
|
||||
);
|
||||
}
|
||||
};
|
||||
}, [refreshProxy, refreshRules, refreshRuleProviders]);
|
||||
|
||||
@@ -243,25 +249,26 @@ export const AppDataProvider = ({
|
||||
"getSystemProxy",
|
||||
getSystemProxy,
|
||||
{
|
||||
revalidateOnFocus: true,
|
||||
revalidateOnReconnect: true,
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
suspense: false,
|
||||
errorRetryCount: 3,
|
||||
errorRetryCount: 2,
|
||||
dedupingInterval: 5000,
|
||||
},
|
||||
);
|
||||
|
||||
const { data: runningMode } = useSWR("getRunningMode", getRunningMode, {
|
||||
revalidateOnFocus: false,
|
||||
suspense: false,
|
||||
errorRetryCount: 3,
|
||||
errorRetryCount: 2,
|
||||
dedupingInterval: 5000,
|
||||
});
|
||||
|
||||
// 高频率更新数据 (2秒)
|
||||
const { data: uptimeData } = useSWR("appUptime", getAppUptime, {
|
||||
// TODO: 运行时间
|
||||
refreshInterval: 2000,
|
||||
refreshInterval: 3000,
|
||||
revalidateOnFocus: false,
|
||||
suspense: false,
|
||||
errorRetryCount: 1,
|
||||
});
|
||||
|
||||
// 提供统一的刷新方法
|
||||
|
||||
@@ -16,15 +16,22 @@ export const WindowProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||
const minimize = useCallback(() => currentWindow.minimize(), [currentWindow]);
|
||||
|
||||
useEffect(() => {
|
||||
let isUnmounted = false;
|
||||
|
||||
const checkMaximized = debounce(async () => {
|
||||
const value = await currentWindow.isMaximized();
|
||||
setMaximized(value);
|
||||
if (!isUnmounted) {
|
||||
const value = await currentWindow.isMaximized();
|
||||
setMaximized(value);
|
||||
}
|
||||
}, 300);
|
||||
|
||||
const unlistenPromise = currentWindow.onResized(checkMaximized);
|
||||
|
||||
return () => {
|
||||
unlistenPromise.then((unlisten) => unlisten());
|
||||
isUnmounted = true;
|
||||
unlistenPromise
|
||||
.then((unlisten) => unlisten())
|
||||
.catch((err) => console.warn("[WindowProvider] 清理监听器失败:", err));
|
||||
};
|
||||
}, [currentWindow]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user