优化内存泄露的问题
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user