mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-12 13:42:08 +08:00
perf(traffic): optimize traffic data handling and improve performance
This commit is contained in:
@@ -6,6 +6,7 @@ type SharedPollerEntry = {
|
||||
timer: number | null;
|
||||
interval: number;
|
||||
callback: (() => void) | null;
|
||||
lastFired: number;
|
||||
refreshWhenHidden: boolean;
|
||||
refreshWhenOffline: boolean;
|
||||
};
|
||||
@@ -32,6 +33,12 @@ const ensureTimer = (key: string, entry: SharedPollerEntry) => {
|
||||
entry.timer = window.setInterval(() => {
|
||||
if (!entry.refreshWhenHidden && isDocumentHidden()) return;
|
||||
if (!entry.refreshWhenOffline && isOffline()) return;
|
||||
const now = Date.now();
|
||||
if (entry.lastFired && now - entry.lastFired < entry.interval / 2) {
|
||||
// Skip duplicate fire within half interval to coalesce concurrent consumers
|
||||
return;
|
||||
}
|
||||
entry.lastFired = now;
|
||||
entry.callback?.();
|
||||
}, entry.interval);
|
||||
};
|
||||
@@ -50,6 +57,7 @@ const registerSharedPoller = (
|
||||
timer: null,
|
||||
interval,
|
||||
callback,
|
||||
lastFired: 0,
|
||||
refreshWhenHidden: options.refreshWhenHidden,
|
||||
refreshWhenOffline: options.refreshWhenOffline,
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import useSWR from "swr";
|
||||
import { getRunningMode, isAdmin, isServiceAvailable } from "@/services/cmds";
|
||||
import { showNotice } from "@/services/notice-service";
|
||||
|
||||
import { useSharedSWRPoller } from "./use-shared-swr-poller";
|
||||
import { useVerge } from "./use-verge";
|
||||
|
||||
export interface SystemState {
|
||||
@@ -43,11 +44,20 @@ export function useSystemState() {
|
||||
},
|
||||
{
|
||||
suspense: true,
|
||||
refreshInterval: 30000,
|
||||
refreshInterval: 0,
|
||||
fallback: defaultSystemState,
|
||||
},
|
||||
);
|
||||
|
||||
useSharedSWRPoller(
|
||||
"getSystemState",
|
||||
30000,
|
||||
() => {
|
||||
void mutateSystemState();
|
||||
},
|
||||
{ refreshWhenHidden: false, refreshWhenOffline: false },
|
||||
);
|
||||
|
||||
const isSidecarMode = systemState.runningMode === "Sidecar";
|
||||
const isServiceMode = systemState.runningMode === "Service";
|
||||
const isTunModeAvailable = systemState.isAdminMode || systemState.isServiceOk;
|
||||
|
||||
@@ -5,10 +5,12 @@ import { useTrafficMonitorEnhanced } from "./use-traffic-monitor";
|
||||
|
||||
const FALLBACK_TRAFFIC: Traffic = { up: 0, down: 0 };
|
||||
|
||||
export const useTrafficData = () => {
|
||||
export const useTrafficData = (options?: { enabled?: boolean }) => {
|
||||
const enabled = options?.enabled ?? true;
|
||||
|
||||
const {
|
||||
graphData: { appendData },
|
||||
} = useTrafficMonitorEnhanced({ subscribe: false });
|
||||
} = useTrafficMonitorEnhanced({ subscribe: false, enabled });
|
||||
const { response, refresh } = useMihomoWsSubscription<ITrafficItem>({
|
||||
storageKey: "mihomo_traffic_date",
|
||||
buildSubscriptKey: (date) => `getClashTraffic-${date}`,
|
||||
|
||||
@@ -343,8 +343,10 @@ const EMPTY_STATS: ISamplerStats = {
|
||||
*/
|
||||
export const useTrafficMonitorEnhanced = (options?: {
|
||||
subscribe?: boolean;
|
||||
enabled?: boolean;
|
||||
}) => {
|
||||
const subscribeToSnapshots = options?.subscribe ?? true;
|
||||
const enabled = options?.enabled ?? true;
|
||||
const [latestSnapshot, setLatestSnapshot] = useState<{
|
||||
availableDataPoints: ITrafficDataPoint[];
|
||||
samplerStats: ISamplerStats;
|
||||
@@ -365,6 +367,8 @@ export const useTrafficMonitorEnhanced = (options?: {
|
||||
|
||||
// 注册引用计数与Worker生命周期
|
||||
useEffect(() => {
|
||||
if (!enabled) return;
|
||||
|
||||
const client = getWorkerClient();
|
||||
clientRef.current = client;
|
||||
|
||||
@@ -396,43 +400,53 @@ export const useTrafficMonitorEnhanced = (options?: {
|
||||
client.stop();
|
||||
}
|
||||
};
|
||||
}, [subscribeToSnapshots]);
|
||||
}, [enabled, subscribeToSnapshots]);
|
||||
|
||||
// Periodically refresh "now" so idle streams age out of the selected window when subscribed
|
||||
useEffect(() => {
|
||||
if (!subscribeToSnapshots) return;
|
||||
if (!enabled || !subscribeToSnapshots) return;
|
||||
|
||||
const timer = window.setInterval(() => {
|
||||
setNow(Date.now());
|
||||
}, 1000);
|
||||
|
||||
return () => window.clearInterval(timer);
|
||||
}, [subscribeToSnapshots]);
|
||||
}, [enabled, subscribeToSnapshots]);
|
||||
|
||||
// 添加流量数据
|
||||
const appendData = useCallback((traffic: Traffic) => {
|
||||
clientRef.current?.appendData(traffic);
|
||||
}, []);
|
||||
const appendData = useCallback(
|
||||
(traffic: Traffic) => {
|
||||
if (!enabled) return;
|
||||
clientRef.current?.appendData(traffic);
|
||||
},
|
||||
[enabled],
|
||||
);
|
||||
|
||||
// 请求不同时间范围的数据
|
||||
const requestRange = useCallback((minutes: number) => {
|
||||
currentRangeRef.current = minutes;
|
||||
setRangeMinutes(minutes);
|
||||
clientRef.current?.setRange(minutes);
|
||||
}, []);
|
||||
const requestRange = useCallback(
|
||||
(minutes: number) => {
|
||||
if (!enabled) return;
|
||||
currentRangeRef.current = minutes;
|
||||
setRangeMinutes(minutes);
|
||||
clientRef.current?.setRange(minutes);
|
||||
},
|
||||
[enabled],
|
||||
);
|
||||
|
||||
// 清空数据
|
||||
const clearData = useCallback(() => {
|
||||
if (!enabled) return;
|
||||
clientRef.current?.clearData();
|
||||
}, []);
|
||||
}, [enabled]);
|
||||
|
||||
const filteredDataPoints = useMemo(() => {
|
||||
if (!enabled) return [];
|
||||
const sourceData = latestSnapshot.availableDataPoints;
|
||||
if (sourceData.length === 0) return [];
|
||||
|
||||
const cutoff = now - rangeMinutes * 60 * 1000;
|
||||
return sourceData.filter((point) => point.timestamp > cutoff);
|
||||
}, [latestSnapshot.availableDataPoints, rangeMinutes, now]);
|
||||
}, [enabled, latestSnapshot.availableDataPoints, rangeMinutes, now]);
|
||||
|
||||
return {
|
||||
graphData: {
|
||||
|
||||
Reference in New Issue
Block a user