优化内存泄露的问题

This commit is contained in:
CN-JS-HuiBai
2026-04-06 02:09:34 +08:00
parent d7b6d3aebb
commit 607d71d1ca
3 changed files with 78 additions and 18 deletions

View File

@@ -111,6 +111,8 @@
// ---- State ----
let previousMetrics = null;
let networkChart = null;
let ws = null; // WebSocket instance
let isWsConnecting = false; // Connection lock
let user = null; // Currently logged in user
let currentServerDetail = { instance: null, job: null, source: null, charts: {} };
let allServersData = [];
@@ -119,6 +121,10 @@
let pageSize = 20;
let currentLatencies = []; // Array of {id, source, dest, latency}
let latencyTimer = null;
let mapResizeHandler = null; // For map cleanup
let siteThemeQuery = null; // For media query cleanup
let siteThemeHandler = null;
let backgroundIntervals = []; // To track setIntervals
// Load sort state from localStorage or use default
let currentSort = { column: 'up', direction: 'desc' };
@@ -357,28 +363,42 @@
// Still populate inputs
dom.pageNameInput.value = window.SITE_SETTINGS.page_name || '';
dom.siteTitleInput.value = window.SITE_SETTINGS.title || '';
dom.logoUrlInput.value = window.SITE_SETTINGS.logo_url || '';
dom.defaultThemeInput.value = window.SITE_SETTINGS.default_theme || 'dark';
dom.show95BandwidthInput.value = window.SITE_SETTINGS.show_95_bandwidth ? "1" : "0";
dom.p95TypeSelect.value = window.SITE_SETTINGS.p95_type || 'tx';
if (dom.siteTitleInput) dom.siteTitleInput.value = window.SITE_SETTINGS.title || '';
if (dom.logoUrlInput) dom.logoUrlInput.value = window.SITE_SETTINGS.logo_url || '';
if (dom.defaultThemeInput) dom.defaultThemeInput.value = window.SITE_SETTINGS.default_theme || 'dark';
if (dom.show95BandwidthInput) dom.show95BandwidthInput.value = window.SITE_SETTINGS.show_95_bandwidth ? "1" : "0";
if (dom.p95TypeSelect) dom.p95TypeSelect.value = window.SITE_SETTINGS.p95_type || 'tx';
// Latency routes loaded separately in openSettings or on startup
}
loadSiteSettings();
// setInterval(fetchMetrics, REFRESH_INTERVAL); - Now using WebSockets
// Track intervals for resource management
initWebSocket();
setInterval(fetchNetworkHistory, NETWORK_HISTORY_INTERVAL);
fetchLatency();
setInterval(fetchLatency, REFRESH_INTERVAL);
backgroundIntervals.push(setInterval(fetchNetworkHistory, NETWORK_HISTORY_INTERVAL));
backgroundIntervals.push(setInterval(fetchLatency, REFRESH_INTERVAL));
}
// ---- Real-time WebSocket ----
function initWebSocket() {
if (isWsConnecting) return;
isWsConnecting = true;
if (ws) {
ws.onmessage = null;
ws.onclose = null;
ws.onerror = null;
ws.close();
}
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}`;
const ws = new WebSocket(wsUrl);
ws = new WebSocket(wsUrl);
ws.onopen = () => {
isWsConnecting = false;
console.log('WS connection established');
};
ws.onmessage = (event) => {
try {
@@ -394,12 +414,13 @@
};
ws.onclose = () => {
isWsConnecting = false;
console.log('WS connection closed. Reconnecting in 5s...');
setTimeout(initWebSocket, 5000);
};
ws.onerror = (err) => {
// Small silent error here to not alert the user constantly if server is down during maintenance
isWsConnecting = false;
ws.close();
};
}
@@ -597,6 +618,13 @@
echarts.registerMap('world', worldJSON);
if (myMap2D) {
myMap2D.dispose();
if (mapResizeHandler) {
window.removeEventListener('resize', mapResizeHandler);
}
}
myMap2D = echarts.init(dom.globeContainer);
const isLight = document.documentElement.classList.contains('light-theme');
@@ -663,7 +691,10 @@
myMap2D.setOption(option);
window.addEventListener('resize', () => myMap2D.resize());
mapResizeHandler = debounce(() => {
if (myMap2D) myMap2D.resize();
}, 100);
window.addEventListener('resize', mapResizeHandler);
if (allServersData.length > 0) {
updateMap2D(allServersData);
@@ -1155,6 +1186,13 @@
// ---- Server Detail ----
async function openServerDetail(instance, job, source) {
// Cleanup old charts if any were still present from a previous open (safety)
if (currentServerDetail.charts) {
Object.values(currentServerDetail.charts).forEach(chart => {
if (chart && chart.destroy) chart.destroy();
});
}
currentServerDetail = { instance, job, source, charts: {} };
dom.serverDetailTitle.textContent = `${job}`;
dom.serverDetailModal.classList.add('active');
@@ -1505,15 +1543,21 @@
const themeToApply = savedTheme || settings.default_theme || 'dark';
applyTheme(themeToApply);
// Listen for system theme changes if set to auto
window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', () => {
// Listen for system theme changes if set to auto (cleanup existing listener first)
if (siteThemeQuery && siteThemeHandler) {
siteThemeQuery.removeEventListener('change', siteThemeHandler);
}
siteThemeQuery = window.matchMedia('(prefers-color-scheme: light)');
siteThemeHandler = () => {
const currentSavedTheme = localStorage.getItem('theme');
const defaultTheme = window.SITE_SETTINGS ? window.SITE_SETTINGS.default_theme : 'dark';
const activeTheme = currentSavedTheme || defaultTheme;
if (activeTheme === 'auto') {
applyTheme('auto');
}
});
};
siteThemeQuery.addEventListener('change', siteThemeHandler);
} catch (err) {
console.error('Error loading site settings:', err);
}

View File

@@ -16,10 +16,11 @@ class AreaChart {
this.dpr = window.devicePixelRatio || 1;
this.padding = { top: 20, right: 16, bottom: 32, left: 56 };
this.currentMaxVal = 0;
this.prevMaxVal = 0;
this.currentMaxVal = 0;
this._resize = this.resize.bind(this);
// Use debounced resize for performance and safety
this._resize = typeof debounce === 'function' ? debounce(this.resize.bind(this), 100) : this.resize.bind(this);
window.addEventListener('resize', this._resize);
this.resize();
}
@@ -335,7 +336,8 @@ class MetricChart {
this.prevMaxVal = 0;
this.currentMaxVal = 0;
this._resize = this.resize.bind(this);
// Use debounced resize for performance and safety
this._resize = typeof debounce === 'function' ? debounce(this.resize.bind(this), 100) : this.resize.bind(this);
window.addEventListener('resize', this._resize);
this.resize();
}

View File

@@ -111,3 +111,17 @@ function animateValue(element, start, end, duration = 600) {
requestAnimationFrame(update);
}
/**
* Debounce function to limit execution frequency
*/
function debounce(fn, delay) {
let timer = null;
return function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay);
};
}