perf: optimize polling, stats cache, and frontend chunk splitting

This commit is contained in:
2026-02-07 11:41:49 +08:00
parent 21c537da10
commit 04b94d7fb2
73 changed files with 516 additions and 203 deletions

View File

@@ -532,6 +532,17 @@ function bindSocket() {
let unbindSocket = null
let statsTimer = null
let kdocsStatusTimer = null
const STATS_POLL_ACTIVE_MS = 10_000
const STATS_POLL_HIDDEN_MS = 30_000
const KDOCS_STATUS_POLL_ACTIVE_MS = 60_000
const KDOCS_STATUS_POLL_HIDDEN_MS = 180_000
function isPageHidden() {
if (typeof document === 'undefined') return false
return document.visibilityState === 'hidden'
}
const shouldPollStats = computed(() => {
// 仅在“真正执行中”才轮询(排队中不轮询,避免空转导致页面闪烁)
@@ -543,15 +554,27 @@ const shouldPollStats = computed(() => {
})
})
function currentStatsPollDelay() {
return isPageHidden() ? STATS_POLL_HIDDEN_MS : STATS_POLL_ACTIVE_MS
}
function stopStatsPolling() {
if (!statsTimer) return
window.clearInterval(statsTimer)
window.clearTimeout(statsTimer)
statsTimer = null
}
function scheduleStatsPolling() {
if (statsTimer || !shouldPollStats.value) return
statsTimer = window.setTimeout(async () => {
statsTimer = null
await refreshStats({ silent: true }).catch(() => {})
scheduleStatsPolling()
}, currentStatsPollDelay())
}
function startStatsPolling() {
if (statsTimer) return
statsTimer = window.setInterval(() => refreshStats({ silent: true }), 10_000)
scheduleStatsPolling()
}
function syncStatsPolling(prevRunning = null) {
@@ -564,11 +587,37 @@ function syncStatsPolling(prevRunning = null) {
else stopStatsPolling()
}
watch(shouldPollStats, (running, prevRunning) => {
watch(shouldPollStats, (_running, prevRunning) => {
syncStatsPolling(prevRunning)
})
let kdocsStatusTimer = null
function currentKdocsStatusPollDelay() {
return isPageHidden() ? KDOCS_STATUS_POLL_HIDDEN_MS : KDOCS_STATUS_POLL_ACTIVE_MS
}
function stopKdocsStatusPolling() {
if (!kdocsStatusTimer) return
window.clearTimeout(kdocsStatusTimer)
kdocsStatusTimer = null
}
function scheduleKdocsStatusPolling() {
if (kdocsStatusTimer) return
kdocsStatusTimer = window.setTimeout(async () => {
kdocsStatusTimer = null
await loadKdocsStatus().catch(() => {})
scheduleKdocsStatusPolling()
}, currentKdocsStatusPollDelay())
}
function restartTimedPollingOnVisibilityChange() {
if (shouldPollStats.value) {
stopStatsPolling()
startStatsPolling()
}
stopKdocsStatusPolling()
scheduleKdocsStatusPolling()
}
onMounted(async () => {
if (!userStore.vipInfo) {
@@ -583,16 +632,17 @@ onMounted(async () => {
await loadKdocsSettings()
await loadKdocsStatus()
await refreshStats()
syncStatsPolling()
// 每60秒刷新 KDocs 状态
kdocsStatusTimer = window.setInterval(() => loadKdocsStatus(), 60_000)
syncStatsPolling()
scheduleKdocsStatusPolling()
window.addEventListener('visibilitychange', restartTimedPollingOnVisibilityChange)
})
onBeforeUnmount(() => {
if (unbindSocket) unbindSocket()
stopStatsPolling()
if (kdocsStatusTimer) window.clearInterval(kdocsStatusTimer)
stopKdocsStatusPolling()
window.removeEventListener('visibilitychange', restartTimedPollingOnVisibilityChange)
})
</script>

View File

@@ -8,6 +8,19 @@ export default defineConfig({
outDir: '../static/app',
emptyOutDir: true,
manifest: true,
cssCodeSplit: true,
chunkSizeWarningLimit: 800,
rollupOptions: {
output: {
manualChunks(id) {
if (!id.includes('node_modules')) return
if (id.includes('/vue/') || id.includes('/vue-router/') || id.includes('/pinia/')) return 'vendor-vue'
if (id.includes('/element-plus/') || id.includes('/@element-plus/')) return 'vendor-element'
if (id.includes('/socket.io-client/')) return 'vendor-socket'
if (id.includes('/axios/')) return 'vendor-axios'
return 'vendor'
},
},
},
},
})