| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- 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;
-
- // 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 dynamicEstimate = Math.max(baseEstimate * 1.5, 10); // 至少10个资源
-
- return Math.floor(dynamicEstimate);
- }
-
- // 更新进度条和状态显示
- function updateProgress() {
- if (!progressBar || !statusDisplay) return;
-
- // 服务端进度权重70%,客户端资源进度权重30%
- const serverWeight = 0.7;
- const clientWeight = 0.3;
-
- const serverPart = serverProgress * serverWeight;
- const clientPart = estimatedTotal > 0 ?
- (resourcesCompleted / estimatedTotal) * clientWeight : 0;
-
- const totalProgress = Math.min((serverPart + clientPart) * 100, 100);
-
- // 更新进度条
- progressBar.style.width = totalProgress + '%';
-
- // 更新状态显示
- if (isLoading && totalProgress < 100) {
- statusDisplay.style.display = 'block';
- statusDisplay.textContent =
- \`Loading... \${Math.round(totalProgress)}% (\${resourcesCompleted}/\${estimatedTotal} resources)\`;
- } else if (totalProgress >= 100) {
- isLoading = false;
- statusDisplay.style.display = 'none';
- progressBar.classList.add('complete');
-
- // 1秒后隐藏进度条
- setTimeout(() => {
- progressBar.style.display = 'none';
- }, 1000);
- }
-
- console.log(\`Progress: Server \${Math.round(serverProgress * 100)}%, Resources \${resourcesCompleted}/\${estimatedTotal}, Total \${Math.round(totalProgress)}%\`);
- }
-
- // 监听服务端编译进度
- import.meta.hot.on('progress:update', (data) => {
- 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++;
-
- // 动态调整预估总数
- if (resourcesCompleted > estimatedTotal * 0.8) {
- estimatedTotal = Math.max(estimatedTotal, resourcesCompleted + 5);
- }
-
- console.log(\`Resource completed: \${entry.name} (type: \${entry.initiatorType})\`);
- updateProgress();
- }
- }
- }
- });
-
- observer.observe({ entryTypes: ['resource'] });
- }
-
- // 监控动态导入
- const originalImport = window.import;
- if (originalImport) {
- window.import = async function(specifier) {
- console.log(\`Dynamic import started: \${specifier}\`);
-
- try {
- const result = await originalImport.call(this, specifier);
- console.log(\`Dynamic import completed: \${specifier}\`);
- return result;
- } catch (error) {
- console.log(\`Dynamic import failed: \${specifier}\`);
- throw error;
- }
- };
- }
-
- // 初始化
- function initialize() {
- estimatedTotal = estimateResourceCount();
- console.log(\`Estimated total resources: \${estimatedTotal}\`);
-
- // 显示初始状态
- if (statusDisplay) {
- statusDisplay.style.display = 'block';
- statusDisplay.textContent = \`Initializing... (0/\${estimatedTotal} resources)\`;
- }
-
- updateProgress();
- }
-
- // 页面加载完成后的处理
- function handlePageLoad() {
- // 给一些时间让所有资源完成加载
- setTimeout(() => {
- if (resourcesCompleted < estimatedTotal * 0.5) {
- // 如果完成的资源太少,调整预估值
- estimatedTotal = Math.max(resourcesCompleted + 2, 5);
- console.log(\`Adjusted estimated total to: \${estimatedTotal}\`);
- }
- updateProgress();
- }, 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);
- }
- }
-
- // 错误处理
- window.addEventListener('error', (event) => {
- console.log(\`Resource load error: \${event.filename || 'unknown'}\`);
- });
-
- // HMR 更新时重置状态
- import.meta.hot.on('vite:beforeUpdate', () => {
- console.log('HMR update detected, maintaining progress tracking...');
- });
- }
- `,
- injectTo: 'body'
- }
- ]
- }
- }
- }
- }
- }
|