export function progressTrackingPlugin() { let server const moduleStats = { total: 0, processed: 0, pending: new Set() } return { name: 'progress-tracking', apply: 'serve', enforce: 'pre', configureServer(_server) { server = _server }, async transform(code, id) { if (server && id) { moduleStats.total++ moduleStats.pending.add(id) server.ws.send('progress:update', { total: moduleStats.total, processed: moduleStats.processed, current: id }) await server.waitForRequestsIdle(id) moduleStats.processed++ moduleStats.pending.delete(id) server.ws.send('progress:update', { total: moduleStats.total, processed: moduleStats.processed, current: null }) } }, transformIndexHtml: { order: 'pre', handler(html, ctx) { return { html, tags: [ { tag: 'style', children: ` #vite-progress-bar { position: fixed; top: 0; left: 0; width: 0%; height: 3px; background: #646cff; transition: width 0.3s ease; z-index: 9999; opacity: 1; } #vite-progress-bar.complete { opacity: 0; transition: opacity 0.5s ease; } #vite-progress-status { position: fixed; top: 10px; right: 10px; background: rgba(0, 0, 0, 0.8); color: white; padding: 8px 12px; border-radius: 4px; font-size: 12px; font-family: monospace; z-index: 10000; display: none; } `, injectTo: 'head' }, { tag: 'div', attrs: { id: 'vite-progress-bar' }, injectTo: 'body-prepend' }, { tag: 'div', attrs: { id: 'vite-progress-status' }, children: 'Loading...', injectTo: 'body-prepend' }, { tag: 'script', attrs: { type: 'module' }, children: ` if (import.meta.hot) { // 进度追踪状态 let serverProgress = 0; let resourcesCompleted = 0; let estimatedTotal = 0; let isLoading = true; let completionTimer = null; let lastResourceTime = Date.now(); let hasServerActivity = false; let serverActivityTimer = null; // DOM 元素引用 const progressBar = document.getElementById('vite-progress-bar'); const statusDisplay = document.getElementById('vite-progress-status'); // 改进的预估算法 function estimateResourceCount() { const scripts = document.querySelectorAll('script[src]').length; const links = document.querySelectorAll('link[href]').length; const styleSheets = document.querySelectorAll('link[rel="stylesheet"]').length; const preloadLinks = document.querySelectorAll('link[rel="preload"], link[rel="modulepreload"]').length; const baseEstimate = scripts + links + styleSheets + preloadLinks; const conservativeEstimate = Math.max(baseEstimate + 10, 15); return conservativeEstimate; } // 检测服务端活动 function detectServerActivity() { hasServerActivity = true; if (serverActivityTimer) clearTimeout(serverActivityTimer); // 如果3秒内没有新的服务端活动,认为服务端编译已完成或无需编译 serverActivityTimer = setTimeout(() => { if (serverProgress === 0) { // console.log('No server compilation detected - using client-only mode'); hasServerActivity = false; } }, 3000); } // 检测是否完成加载 function checkCompletion() { const now = Date.now(); const timeSinceLastResource = now - lastResourceTime; // 添加详细的调试日志 // console.log('=== checkCompletion Debug ==='); // console.log('hasServerActivity:', hasServerActivity); // console.log('serverProgress:', serverProgress); // console.log('resourcesCompleted:', resourcesCompleted); // console.log('estimatedTotal:', estimatedTotal); // console.log('timeSinceLastResource:', timeSinceLastResource); // console.log('isLoading:', isLoading); // 如果已经不在加载状态,直接返回 if (!isLoading) { // console.log('⚠️ Not loading anymore, exiting checkCompletion'); return; } // 根据服务端活动情况调整完成条件 if (!hasServerActivity) { // 无服务端编译时,主要依赖客户端资源 if (resourcesCompleted >= estimatedTotal * 0.8 && timeSinceLastResource > 2000) { // console.log('Client-only loading complete'); forceComplete(); return; } } else { // 有服务端编译时,需要服务端完成 if (serverProgress >= 1.0 && timeSinceLastResource > 2000) { // console.log('Server + client loading complete'); forceComplete(); return; } } // 超时强制完成 if (timeSinceLastResource > 1000 * 10) { // console.log('Force completing due to timeout'); forceComplete(); } // 如果仍在加载中,且未触发超时强制完成,则500ms后再次检查 if (isLoading && timeSinceLastResource <= 1000 * 10) { setTimeout(checkCompletion, 1000 * 5); // 循环检查 } } // 强制完成 function forceComplete() { if (!isLoading) return; isLoading = false; progressBar.style.width = '100%'; statusDisplay.style.display = 'none'; progressBar.classList.add('complete'); // console.log('Loading completed!'); setTimeout(() => { progressBar.style.display = 'none'; }, 1000); } // 更新进度条和状态显示 function updateProgress() { if (!progressBar || !statusDisplay || !isLoading) return; let totalProgress; if (!hasServerActivity && serverProgress === 0) { // 无服务端编译活动,100%依赖客户端资源 const clientProgress = estimatedTotal > 0 ? Math.min(resourcesCompleted / estimatedTotal, 1.0) : 0; totalProgress = clientProgress * 100; // console.log(\`Client-only mode: \${resourcesCompleted}/\${estimatedTotal} resources\`); } else { // 有服务端编译活动,使用混合权重 const serverWeight = serverProgress < 1.0 ? 0.6 : 0.3; const clientWeight = serverProgress < 1.0 ? 0.4 : 0.7; const serverPart = serverProgress * serverWeight; const clientPart = estimatedTotal > 0 ? Math.min(resourcesCompleted / estimatedTotal, 1.0) * clientWeight : 0; totalProgress = (serverPart + clientPart) * 100; // console.log(\`Mixed mode: Server \${Math.round(serverProgress * 100)}%, Client \${resourcesCompleted}/\${estimatedTotal}\`); } // 确保进度不会倒退 const currentWidth = parseFloat(progressBar.style.width) || 0; totalProgress = Math.max(totalProgress, currentWidth); // 更新进度条 progressBar.style.width = Math.min(totalProgress, 100) + '%'; // 更新状态显示 statusDisplay.style.display = 'block'; const mode = hasServerActivity ? 'Mixed' : 'Client-only'; statusDisplay.textContent = \`Loading... \${Math.round(totalProgress)}% (\${mode}: \${resourcesCompleted}/\${estimatedTotal})\`; // console.log(\`Progress: Total \${Math.round(totalProgress)}%\`); // 如果接近完成,开始检测完成状态 if (totalProgress >= 90) { if (completionTimer) clearTimeout(completionTimer); completionTimer = setTimeout(checkCompletion, 1000); } } // 监听服务端编译进度 import.meta.hot.on('progress:update', (data) => { detectServerActivity(); serverProgress = data.total > 0 ? data.processed / data.total : 0; updateProgress(); }); // 使用 PerformanceObserver 监控资源加载完成 if (window.PerformanceObserver) { const completedResources = new Set(); const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.entryType === 'resource') { const isRelevantResource = entry.initiatorType === 'script' || entry.initiatorType === 'link' || entry.initiatorType === 'css' || entry.name.includes('.js') || entry.name.includes('.css') || entry.name.includes('.ts'); if (isRelevantResource && !completedResources.has(entry.name)) { completedResources.add(entry.name); resourcesCompleted++; lastResourceTime = Date.now(); // 动态调整预估总数 if (resourcesCompleted > estimatedTotal * 0.9) { estimatedTotal = Math.max(estimatedTotal, resourcesCompleted + 3); } // console.log(\`Resource completed: \${entry.name} (type: \${entry.initiatorType})\`); updateProgress(); } } } }); observer.observe({ entryTypes: ['resource'] }); } // 初始化 function initialize() { estimatedTotal = estimateResourceCount(); lastResourceTime = Date.now(); // console.log(\`Estimated total resources: \${estimatedTotal}\`); // 显示初始状态 if (statusDisplay) { statusDisplay.style.display = 'block'; statusDisplay.textContent = \`Initializing... (0/\${estimatedTotal} resources)\`; } updateProgress(); // 启动服务端活动检测 setTimeout(() => { if (!hasServerActivity) { // console.log('No server activity detected, switching to client-only mode'); updateProgress(); } }, 1000); } // 页面加载完成后的处理 function handlePageLoad() { setTimeout(() => { if (resourcesCompleted > 0) { estimatedTotal = Math.max(resourcesCompleted + 2, estimatedTotal); // console.log(\`Final estimated total: \${estimatedTotal}\`); } updateProgress(); // 开始完成检测 setTimeout(checkCompletion, 2000); }, 1000); } // 根据文档状态初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initialize); document.addEventListener('load', handlePageLoad); } else { setTimeout(initialize, 0); if (document.readyState === 'complete') { setTimeout(handlePageLoad, 100); } else { document.addEventListener('load', handlePageLoad); } } // HMR 更新时重置状态 import.meta.hot.on('vite:beforeUpdate', () => { // console.log('HMR update detected, maintaining progress tracking...'); }); } `, injectTo: 'body' } ] } } } } }