// ==UserScript== // @name 网页资源优先级优化器 // @name:en Web Resource Priority Optimizer // @namespace https://github.com/web-resource-optimizer // @version 1.0.0 // @description 自动优化网页资源(JS、CSS、图片、字体)的加载优先级,降低阻塞时间,提升页面性能 // @description:en Automatically optimize web resource (JS, CSS, images, fonts) loading priority to reduce blocking time and improve page performance // @author moyu001 // @license MIT // @match *://*/* // @grant GM_log // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_listValues // @grant GM_addStyle // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_setClipboard // @grant GM_notification // @grant GM_xmlhttpRequest // @grant unsafeWindow // @run-at document-start // @inject-into page // @sandbox JavaScript // @downloadURL https://update.greasyfork.icu/scripts/541922/%E7%BD%91%E9%A1%B5%E8%B5%84%E6%BA%90%E4%BC%98%E5%85%88%E7%BA%A7%E4%BC%98%E5%8C%96%E5%99%A8.user.js // @updateURL https://update.greasyfork.icu/scripts/541922/%E7%BD%91%E9%A1%B5%E8%B5%84%E6%BA%90%E4%BC%98%E5%85%88%E7%BA%A7%E4%BC%98%E5%8C%96%E5%99%A8.meta.js // ==/UserScript== /** * 网页资源优先级优化器 * Web Resource Priority Optimizer * * 基于行业最佳实践,自动优化网页资源加载优先级 * 支持JS、CSS、图片、字体的智能优化 * 兼容主流CDN和跨站资源 * 提供低侵入、可回退的安全优化方案 */ (function() { 'use strict'; // 全局配置 const CONFIG = { // 版本信息 VERSION: '1.0.0', NAME: 'Web Resource Priority Optimizer', // 调试配置 DEBUG: false, LOG_LEVEL: 'INFO', // ERROR, WARN, INFO, DEBUG // 功能开关 FEATURES: { JS_OPTIMIZATION: true, CSS_OPTIMIZATION: true, IMAGE_OPTIMIZATION: true, FONT_OPTIMIZATION: true, CDN_OPTIMIZATION: true, CROSS_ORIGIN_OPTIMIZATION: true, DYNAMIC_MONITORING: true }, // 性能配置 PERFORMANCE: { DELAY_LOADING: 1000, // 延迟加载时间(毫秒) LAZY_LOADING_THRESHOLD: 0.1, // 懒加载阈值 PRELOAD_PRIORITY: 'high', // 预加载优先级 MAX_CONCURRENT_LOADS: 6 // 最大并发加载数 }, // 安全配置 SECURITY: { ENABLE_SRI: true, FORCE_HTTPS: true, CORS_STRICT: false, CSP_COMPATIBLE: true }, // CDN配置 CDN: { ENABLED: true, PATTERNS: { jsdelivr: /cdn\.jsdelivr\.net/i, unpkg: /unpkg\.com/i, cdnjs: /cdnjs\.cloudflare\.com/i, google: /ajax\.googleapis\.com/i, googleFonts: /fonts\.googleapis\.com|fonts\.gstatic\.com/i, bootstrap: /stackpath\.bootstrapcdn\.com/i, fontawesome: /use\.fontawesome\.com/i, typekit: /use\.typekit\.net/i, iconify: /api\.iconify\.design/i } }, // 白名单和黑名单 LISTS: { WHITELIST: [], // 白名单网站(完全跳过优化) BLACKLIST: [], // 黑名单网站(禁用特定优化) CRITICAL_RESOURCES: [] // 关键资源列表 } }; // 全局状态管理 const STATE = { // 初始化状态 initialized: false, enabled: true, // 优化统计 stats: { optimizedScripts: 0, optimizedStyles: 0, optimizedImages: 0, optimizedFonts: 0, cdnResources: 0, crossOriginResources: 0, errors: 0, warnings: 0, criticalProtected: 0, cdnResourcesPreloaded: 0, spaOptimized: 0, dynamicCDNOptimized: 0 }, // 原始状态记录(用于回退) originalStates: { scripts: new Map(), styles: new Map(), images: new Map(), fonts: new Map() }, // 性能监控 performance: { startTime: performance.now(), loadTimes: new Map(), errorRates: new Map() }, // 自动回退配置和状态 autoRollback: { enabled: true, fatalErrorCount: 0, fatalErrorThreshold: 3, lastRollbackReason: '', hasRolledBack: false } }; // 日志系统 const Logger = { levels: { ERROR: 0, WARN: 1, INFO: 2, DEBUG: 3 }, currentLevel: CONFIG.LOG_LEVEL, // 日志历史队列 history: [], maxHistory: 500, log: function(level, message, data = null) { if (this.levels[level] <= this.levels[this.currentLevel]) { const timestamp = new Date().toISOString(); const logMessage = `[${CONFIG.NAME}] [${level}] [${timestamp}] ${message}`; // 写入内存日志队列 this.history.push({ level, timestamp, message, data: data ? JSON.stringify(data) : '' }); if (this.history.length > this.maxHistory) { this.history.shift(); } if (CONFIG.DEBUG) { console.log(logMessage, data || ''); } // 保存到GM_log(如果可用) if (typeof GM_log !== 'undefined') { GM_log(logMessage); } } }, error: function(message, data) { this.log('ERROR', message, data); STATE.stats.errors++; if (STATE.autoRollback.enabled) { STATE.autoRollback.fatalErrorCount++; if (STATE.autoRollback.fatalErrorCount >= STATE.autoRollback.fatalErrorThreshold && !STATE.autoRollback.hasRolledBack) { if (window.WebResourceOptimizer && window.WebResourceOptimizer.controller) { window.WebResourceOptimizer.controller.rollbackAll(); STATE.autoRollback.lastRollbackReason = `自动回退:累计致命错误达到${STATE.autoRollback.fatalErrorCount}次`; STATE.autoRollback.hasRolledBack = true; STATE.enabled = false; Logger.info(STATE.autoRollback.lastRollbackReason + ',优化已禁用'); } } } }, warn: function(message, data) { this.log('WARN', message, data); STATE.stats.warnings++; }, info: function(message, data) { this.log('INFO', message, data); }, debug: function(message, data) { this.log('DEBUG', message, data); }, // 获取详细日志历史 getHistory: function() { return this.history.map(entry => `[${entry.timestamp}] [${entry.level}] ${entry.message}${entry.data ? ' | ' + entry.data : ''}` ); } }; // 调试开关和日志输出模块 const DebugModule = { // 调试面板 panel: null, // 初始化调试模块 init: function() { this.setupGlobalSwitches(); this.createDebugPanel(); this.setupMenuCommands(); this.setupKeyboardShortcuts(); Logger.info('Debug module initialized'); }, // 设置全局开关 setupGlobalSwitches: function() { // 全局调试开关 Object.defineProperty(window, 'RESOURCE_OPTIMIZER_DEBUG', { get: function() { return CONFIG.DEBUG; }, set: function(value) { CONFIG.DEBUG = Boolean(value); DebugModule.updateDebugMode(); Logger.info(`Debug mode ${CONFIG.DEBUG ? 'enabled' : 'disabled'}`); }, configurable: true }); // 全局禁用开关 Object.defineProperty(window, 'DISABLE_RESOURCE_OPTIMIZER', { get: function() { return !STATE.enabled; }, set: function(value) { STATE.enabled = !Boolean(value); if (controller) { controller.setEnabled(STATE.enabled); } Logger.info(`Resource optimizer ${STATE.enabled ? 'enabled' : 'disabled'}`); }, configurable: true }); // 日志级别控制 Object.defineProperty(window, 'RESOURCE_OPTIMIZER_LOG_LEVEL', { get: function() { return Logger.currentLevel; }, set: function(level) { if (Logger.levels.hasOwnProperty(level)) { Logger.currentLevel = level; Logger.info(`Log level changed to ${level}`); } else { Logger.warn(`Invalid log level: ${level}`); } }, configurable: true }); }, // 创建调试面板 createDebugPanel: function() { if (!CONFIG.DEBUG) return; // 创建面板容器 this.panel = document.createElement('div'); this.panel.id = 'wro-debug-panel'; this.panel.style.cssText = ` position: fixed; top: 10px; right: 10px; width: 300px; max-height: 400px; background: rgba(0, 0, 0, 0.9); color: #fff; font-family: monospace; font-size: 12px; padding: 10px; border-radius: 5px; z-index: 999999; overflow-y: auto; display: none; box-shadow: 0 2px 8px #0008; user-select: none; `; // 拖拽相关 this.panel.onmousedown = function(e) { if (e.target.className !== 'wro-debug-panel-drag') return; const panel = DebugModule.panel; let startX = e.clientX, startY = e.clientY; let rect = panel.getBoundingClientRect(); let offsetX = startX - rect.left, offsetY = startY - rect.top; function onMove(ev) { panel.style.left = (ev.clientX - offsetX) + 'px'; panel.style.top = (ev.clientY - offsetY) + 'px'; panel.style.right = 'auto'; } function onUp() { document.removeEventListener('mousemove', onMove); document.removeEventListener('mouseup', onUp); } document.addEventListener('mousemove', onMove); document.addEventListener('mouseup', onUp); e.preventDefault(); }; // 创建面板内容 this.updatePanelContent(); // 添加到页面 document.body.appendChild(this.panel); // 创建切换按钮 this.createToggleButton(); }, // 创建切换按钮 createToggleButton: function() { const button = document.createElement('button'); button.id = 'wro-debug-toggle'; button.textContent = 'WRO Debug'; button.style.cssText = ` position: fixed; top: 10px; right: 10px; background: #007acc; color: white; border: none; padding: 5px 10px; border-radius: 3px; cursor: pointer; font-size: 12px; z-index: 999998; `; button.addEventListener('click', () => { this.togglePanel(); }); document.body.appendChild(button); }, // 切换面板显示 togglePanel: function() { if (this.panel) { this.panel.style.display = this.panel.style.display === 'none' ? 'block' : 'none'; if (this.panel.style.display === 'block') { this.updatePanelContent(); } } }, // 更新面板内容 updatePanelContent: function() { if (!this.panel) return; const stats = controller ? controller.getStats() : STATE.stats; const filterLevel = this.logFilterLevel || 'ALL'; const filterKeyword = this.logFilterKeyword || ''; let logs = Logger.getHistory(); if (filterLevel !== 'ALL') { logs = logs.filter(l => l.includes(`[${filterLevel}]`)); } if (filterKeyword) { logs = logs.filter(l => l.toLowerCase().includes(filterKeyword.toLowerCase())); } const logHtml = logs.length > 0 ? `
${logs.map(l => `
${l}
`).join('')}
` : '
无日志
'; // 最小化状态 const minimized = this.minimized; const autoRollbackMsg = STATE.autoRollback.lastRollbackReason ? `
⚠️ ${STATE.autoRollback.lastRollbackReason}
` : ''; const autoRollbackConfig = `
阈值: 已触发:${STATE.autoRollback.fatalErrorCount} 已回退:${STATE.autoRollback.hasRolledBack ? '是' : '否'}
`; const showRestoreBtn = STATE.autoRollback.hasRolledBack || !STATE.enabled; const restoreBtnHtml = showRestoreBtn ? `` : ''; const compatMsg = COMPAT_WARNINGS.length > 0 ? `
⚠️ 兼容性警告:${COMPAT_WARNINGS.join(',')}
` : ''; this.panel.innerHTML = `
Web Resource Optimizer Debug
${compatMsg} ${autoRollbackMsg} ${autoRollbackConfig} ${restoreBtnHtml} ${minimized ? '' : `
Status: ${STATE.enabled ? 'Enabled' : 'Disabled'}
Debug: ${CONFIG.DEBUG ? 'On' : 'Off'}
Log Level: ${Logger.currentLevel}
Uptime: ${Math.round((performance.now() - STATE.performance.startTime) / 1000)}s
Optimization Stats:
Scripts: ${stats.optimizedScripts}
Styles: ${stats.optimizedStyles}
Images: ${stats.optimizedImages}
Fonts: ${stats.optimizedFonts}
CDN Resources: ${stats.cdnResources}
Cross-Origin: ${stats.crossOriginResources}
Errors: ${stats.errors}
Warnings: ${stats.warnings}
Quick Actions:
详细日志:
${logHtml}
`} `; setTimeout(() => { const logbox = document.getElementById('wro-debug-logbox'); if (logbox) logbox.scrollTop = logbox.scrollHeight; const levelSel = document.getElementById('wro-log-filter-level'); if (levelSel) { levelSel.onchange = (e) => { DebugModule.logFilterLevel = e.target.value; DebugModule.updatePanelContent(); }; } const kwInput = document.getElementById('wro-log-filter-keyword'); if (kwInput) { kwInput.oninput = (e) => { DebugModule.logFilterKeyword = e.target.value; DebugModule.updatePanelContent(); }; } const clearBtn = document.getElementById('wro-log-clear-btn'); if (clearBtn) { clearBtn.onclick = () => { Logger.history = []; DebugModule.updatePanelContent(); }; } const rollbackBtn = document.querySelector('#wro-debug-panel button[onclick*="rollbackAll"]'); if (rollbackBtn) { rollbackBtn.onclick = () => { DebugModule.rollbackAll(); }; } // 自动回退配置事件绑定 const enableChk = document.getElementById('wro-auto-rollback-enable'); if (enableChk) { enableChk.onchange = (e) => { STATE.autoRollback.enabled = !!e.target.checked; DebugModule.updatePanelContent(); }; } const thresholdInput = document.getElementById('wro-auto-rollback-threshold'); if (thresholdInput) { thresholdInput.onchange = (e) => { let v = parseInt(e.target.value, 10); if (isNaN(v) || v < 1) v = 1; STATE.autoRollback.fatalErrorThreshold = v; DebugModule.updatePanelContent(); }; } // 重新启用优化按钮事件绑定 const restoreBtn = document.getElementById('wro-restore-optimizer-btn'); if (restoreBtn) { restoreBtn.onclick = () => { STATE.autoRollback.hasRolledBack = false; STATE.autoRollback.fatalErrorCount = 0; STATE.autoRollback.lastRollbackReason = ''; STATE.enabled = true; Logger.info('用户手动重新启用优化'); DebugModule.updatePanelContent(); }; } }, 0); }, // 设置菜单命令 setupMenuCommands: function() { if (typeof GM_registerMenuCommand !== 'undefined') { // 切换调试模式 GM_registerMenuCommand('Toggle Debug Mode', () => { CONFIG.DEBUG = !CONFIG.DEBUG; this.updateDebugMode(); Logger.info(`Debug mode ${CONFIG.DEBUG ? 'enabled' : 'disabled'} via menu`); }); // 切换优化器 GM_registerMenuCommand('Toggle Optimizer', () => { STATE.enabled = !STATE.enabled; if (controller) { controller.setEnabled(STATE.enabled); } Logger.info(`Resource optimizer ${STATE.enabled ? 'enabled' : 'disabled'} via menu`); }); // 重置统计 GM_registerMenuCommand('Reset Statistics', () => { if (controller) { controller.resetStats(); } Logger.info('Statistics reset via menu'); }); // 导出日志 GM_registerMenuCommand('Export Logs', () => { this.exportLogs(); }); // 显示调试面板 GM_registerMenuCommand('Show Debug Panel', () => { this.showDebugPanel(); }); } }, // 设置键盘快捷键 setupKeyboardShortcuts: function() { document.addEventListener('keydown', (event) => { // Ctrl+Shift+D: 切换调试模式 if (event.ctrlKey && event.shiftKey && event.key === 'D') { event.preventDefault(); CONFIG.DEBUG = !CONFIG.DEBUG; this.updateDebugMode(); Logger.info(`Debug mode ${CONFIG.DEBUG ? 'enabled' : 'disabled'} via keyboard shortcut`); } // Ctrl+Shift+O: 切换优化器 if (event.ctrlKey && event.shiftKey && event.key === 'O') { event.preventDefault(); STATE.enabled = !STATE.enabled; if (controller) { controller.setEnabled(STATE.enabled); } Logger.info(`Resource optimizer ${STATE.enabled ? 'enabled' : 'disabled'} via keyboard shortcut`); } // Ctrl+Shift+P: 显示调试面板 if (event.ctrlKey && event.shiftKey && event.key === 'P') { event.preventDefault(); this.showDebugPanel(); } }); }, // 更新调试模式 updateDebugMode: function() { if (CONFIG.DEBUG) { this.showDebugPanel(); this.enableVerboseLogging(); } else { this.hideDebugPanel(); this.disableVerboseLogging(); } }, // 显示调试面板 showDebugPanel: function() { if (this.panel) { this.panel.style.display = 'block'; this.updatePanelContent(); } }, // 隐藏调试面板 hideDebugPanel: function() { if (this.panel) { this.panel.style.display = 'none'; } }, // 启用详细日志 enableVerboseLogging: function() { Logger.currentLevel = 'DEBUG'; Logger.info('Verbose logging enabled'); }, // 禁用详细日志 disableVerboseLogging: function() { Logger.currentLevel = 'INFO'; Logger.info('Verbose logging disabled'); }, // 导出日志 exportLogs: function() { const logs = this.getLogHistory(); const logText = logs.join('\n'); // 创建下载链接 const blob = new Blob([logText], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `wro-logs-${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.txt`; a.click(); URL.revokeObjectURL(url); Logger.info('Logs exported successfully'); }, // 获取日志历史 getLogHistory: function() { // 返回详细日志历史 return Logger.getHistory(); }, // 重置统计 resetStats: function() { if (controller) { controller.resetStats(); } this.updatePanelContent(); Logger.info('Statistics reset'); }, // 回退所有优化 rollbackAll: function() { let count = 0; for (const [el] of STATE.originalStates.scripts) { if (el && typeof el.tagName === 'string') { if (typeof this.optimizers?.[0]?.rollback === 'function') { if (this.optimizers[0].rollback(el)) count++; } } } for (const [el] of STATE.originalStates.styles) { if (el && typeof el.tagName === 'string') { if (typeof this.optimizers?.[0]?.rollback === 'function') { if (this.optimizers[0].rollback(el)) count++; } } } for (const [el] of STATE.originalStates.fonts) { if (el && typeof el.tagName === 'string') { if (typeof this.optimizers?.[0]?.rollback === 'function') { if (this.optimizers[0].rollback(el)) count++; } } } for (const [el] of STATE.originalStates.images) { if (el && typeof el.tagName === 'string') { if (typeof this.optimizers?.[0]?.rollback === 'function') { if (this.optimizers[0].rollback(el)) count++; } } } Logger.info(`Rollback all: ${count} elements restored`); return count; } }; // 工具函数 const Utils = { // 检查是否为关键资源 isCriticalResource: function(element, resourceType) { // data-critical属性 if (element.hasAttribute('data-critical') && element.getAttribute('data-critical') === 'true') { Logger.info(`[关键资源保护] data-critical: ${element.tagName} ${element.src || element.href || element.outerHTML}`); STATE.stats.criticalProtected = (STATE.stats.criticalProtected || 0) + 1; return true; } // 内联脚本/样式 if ((element.tagName === 'SCRIPT' && !element.src) || (element.tagName === 'STYLE')) { Logger.info(`[关键资源保护] 内联: ${element.tagName}`); STATE.stats.criticalProtected = (STATE.stats.criticalProtected || 0) + 1; return true; } // 白名单 const url = element.src || element.href || ''; if (CONFIG.LISTS.WHITELIST.some(domain => url.includes(domain))) { Logger.info(`[关键资源保护] 白名单: ${url}`); STATE.stats.criticalProtected = (STATE.stats.criticalProtected || 0) + 1; return true; } // 首屏资源(粗略:在视口内的img、首屏css/js) if (['img', 'css', 'js', 'font'].includes(resourceType)) { try { if (element.getBoundingClientRect) { const rect = element.getBoundingClientRect(); if (rect.top >= 0 && rect.top < window.innerHeight * 1.2) { Logger.info(`[关键资源保护] 首屏: ${element.tagName} ${url}`); STATE.stats.criticalProtected = (STATE.stats.criticalProtected || 0) + 1; return true; } } } catch (e) {} } // 常见关键字(react、vue、main、logo、core、polyfill等) const keywords = ['react', 'vue', 'main', 'core', 'polyfill', 'logo', 'chunk-vendors', 'runtime', 'manifest', 'bootstrap', 'jquery', 'angular', 'zone.js']; if (keywords.some(kw => url.toLowerCase().includes(kw))) { Logger.info(`[关键资源保护] 关键字: ${url}`); STATE.stats.criticalProtected = (STATE.stats.criticalProtected || 0) + 1; return true; } return false; }, // 检查元素是否在视口内 isInViewport: function(element) { if (!element) return false; const rect = element.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); }, // 检查是否为CDN资源 isCDNResource: function(url) { if (!url) return { isCDN: false, provider: null, type: null }; // CDN域名正则 const CDN_PATTERNS = CONFIG.CDN.PATTERNS; // 路径特征正则 const PATH_PATTERNS = { npm: /\/npm\//, github: /\/gh\//, ajaxLibs: /\/ajax\/libs\//, bootstrap: /bootstrap/i, fontawesome: /font-awesome|all\.css/i, googleFonts: /css2\?family=|gstatic\.com\/s\//i }; // 文件类型正则 const FILE_TYPES = { js: /\.(js|mjs)$/i, css: /\.(css|scss|sass)$/i, font: /\.(woff|woff2|ttf|eot|otf)$/i, image: /\.(svg|png|jpg|jpeg|gif|webp|avif)$/i }; // 检查CDN域名 for (const [provider, pattern] of Object.entries(CDN_PATTERNS)) { if (pattern.test(url)) { // 检查文件类型 let type = null; for (const [ftype, fpattern] of Object.entries(FILE_TYPES)) { if (fpattern.test(url)) { type = ftype; break; } } // 检查路径特征 let pathMatch = null; for (const [pname, ppattern] of Object.entries(PATH_PATTERNS)) { if (ppattern.test(url)) { pathMatch = pname; break; } } return { isCDN: true, provider, type, pathMatch }; } } return { isCDN: false, provider: null, type: null, pathMatch: null }; }, // 检查是否为跨站资源 isCrossOriginResource: function(url) { if (!url) return false; try { const urlObj = new URL(url, window.location.href); return urlObj.origin !== window.location.origin; } catch (error) { return false; } }, // 安全的DOM操作 safeSetAttribute: function(element, attribute, value) { try { element.setAttribute(attribute, value); return true; } catch (error) { Logger.warn(`Failed to set attribute ${attribute} on element`, error); return false; } }, // 安全的DOM移除 safeRemoveAttribute: function(element, attribute) { try { element.removeAttribute(attribute); return true; } catch (error) { Logger.warn(`Failed to remove attribute ${attribute} on element`, error); return false; } }, // 生成唯一ID generateId: function() { return 'wro_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); } }; // 资源优化器基类 class ResourceOptimizer { constructor() { this.name = 'ResourceOptimizer'; this.enabled = true; } // 初始化优化器 init() { Logger.info(`${this.name} initialized`); } // 优化资源 optimize(element) { if (!this.enabled || !element) return false; try { this.recordOriginalState(element); attachResourceErrorHandler(element); return this.doOptimize(element); } catch (error) { Logger.error(`${this.name} optimization failed`, error); return false; } } // 记录原始属性(子类可扩展) recordOriginalState(element) { if (!element || !element.tagName) return; const tag = element.tagName.toUpperCase(); if (tag === 'SCRIPT' && element.src) { if (!STATE.originalStates.scripts.has(element)) { STATE.originalStates.scripts.set(element, { src: element.src, type: element.type, async: element.async, defer: element.defer, crossorigin: element.crossOrigin, attributes: [...element.attributes].map(a => [a.name, a.value]) }); } } else if (tag === 'LINK' && element.href) { if (element.rel === 'stylesheet' || element.rel === 'preload' || element.href.includes('font')) { if (!STATE.originalStates.styles.has(element)) { STATE.originalStates.styles.set(element, { href: element.href, rel: element.rel, as: element.as, media: element.media, crossorigin: element.crossOrigin, attributes: [...element.attributes].map(a => [a.name, a.value]) }); } } else if (element.rel === 'preload' && element.as === 'font') { if (!STATE.originalStates.fonts.has(element)) { STATE.originalStates.fonts.set(element, { href: element.href, rel: element.rel, as: element.as, crossorigin: element.crossOrigin, attributes: [...element.attributes].map(a => [a.name, a.value]) }); } } } else if (tag === 'IMG' && element.src) { if (!STATE.originalStates.images.has(element)) { STATE.originalStates.images.set(element, { src: element.src, loading: element.loading, crossorigin: element.crossOrigin, attributes: [...element.attributes].map(a => [a.name, a.value]) }); } } } // 具体的优化逻辑(子类实现) doOptimize(element) { Logger.warn(`${this.name} doOptimize method not implemented`); return false; } // 回退优化 rollback(element) { if (!element) return false; try { return this.doRollback(element); } catch (error) { Logger.error(`${this.name} rollback failed`, error); return false; } } // 具体的回退逻辑(基类实现通用属性恢复,子类可扩展) doRollback(element) { if (!element || !element.tagName) return false; const tag = element.tagName.toUpperCase(); let state; if (tag === 'SCRIPT') { state = STATE.originalStates.scripts.get(element); if (state) { element.src = state.src; element.type = state.type; element.async = state.async; element.defer = state.defer; element.crossOrigin = state.crossorigin; // 恢复所有原始属性 for (const [k, v] of state.attributes) { element.setAttribute(k, v); } Logger.info('Script element rolled back', element.src); return true; } } else if (tag === 'LINK') { state = STATE.originalStates.styles.get(element) || STATE.originalStates.fonts.get(element); if (state) { element.href = state.href; element.rel = state.rel; element.as = state.as; element.media = state.media; element.crossOrigin = state.crossorigin; for (const [k, v] of state.attributes) { element.setAttribute(k, v); } Logger.info('Link element rolled back', element.href); return true; } } else if (tag === 'IMG') { state = STATE.originalStates.images.get(element); if (state) { element.src = state.src; element.loading = state.loading; element.crossOrigin = state.crossorigin; for (const [k, v] of state.attributes) { element.setAttribute(k, v); } Logger.info('Image element rolled back', element.src); return true; } } Logger.warn('No original state found for rollback', element); return false; } } // 主控制器 class ResourceOptimizerController { constructor() { this.optimizers = []; this.observer = null; this.initialized = false; } // 初始化控制器 init() { if (this.initialized) return; Logger.info('Initializing Resource Optimizer Controller'); // 检查是否被禁用 if (window.DISABLE_RESOURCE_OPTIMIZER) { Logger.info('Resource optimizer disabled by user'); return; } // 检查调试模式 if (window.RESOURCE_OPTIMIZER_DEBUG) { CONFIG.DEBUG = true; Logger.info('Debug mode enabled'); } // 初始化优化器 this.initOptimizers(); // 开始监控 this.startMonitoring(); // SPA兼容 setupSPARouterCompatibility(this); this.initialized = true; Logger.info('Resource Optimizer Controller initialized successfully'); } // 初始化优化器 initOptimizers() { // 这里将在后续任务中实现具体的优化器 Logger.info('Optimizers will be initialized in subsequent tasks'); } // 开始监控 startMonitoring() { // 监控现有资源 this.optimizeExistingResources(); // 监控动态添加的资源 this.startDynamicMonitoring(); } // 优化现有资源 optimizeExistingResources() { Logger.info('Optimizing existing resources'); // 优化脚本 if (CONFIG.FEATURES.JS_OPTIMIZATION) { this.optimizeScripts(); } // 优化样式 if (CONFIG.FEATURES.CSS_OPTIMIZATION) { this.optimizeStyles(); } // 优化图片 if (CONFIG.FEATURES.IMAGE_OPTIMIZATION) { this.optimizeImages(); } // 优化字体 if (CONFIG.FEATURES.FONT_OPTIMIZATION) { this.optimizeFonts(); } } // 优化脚本 optimizeScripts() { const scripts = document.querySelectorAll('script[src]'); Logger.info(`Found ${scripts.length} script elements to optimize`); scripts.forEach(script => { const cdnInfo = Utils.isCDNResource(script.src); if (cdnInfo.isCDN) { STATE.stats.cdnResources++; Logger.info(`[CDN识别] JS: ${script.src} | provider: ${cdnInfo.provider} | type: ${cdnInfo.type}`); // === CDN资源预加载优化 === if (!Utils.isCriticalResource(script, 'js') && !document.querySelector(`link[rel="preload"][href="${script.src}"]`)) { const preload = document.createElement('link'); preload.rel = 'preload'; preload.as = 'script'; preload.href = script.src; preload.crossOrigin = 'anonymous'; document.head.appendChild(preload); STATE.stats.cdnResourcesPreloaded = (STATE.stats.cdnResourcesPreloaded || 0) + 1; STATE.stats.dynamicCDNOptimized = (STATE.stats.dynamicCDNOptimized || 0) + 1; Logger.info(`[CDN优化] 已为CDN JS资源添加preload: ${script.src}`); } } // === 跨站资源CORS安全处理 === if (Utils.isCrossOriginResource(script.src) && !script.hasAttribute('crossorigin') && !Utils.isCriticalResource(script, 'js')) { script.setAttribute('crossorigin', 'anonymous'); STATE.stats.crossOriginResources++; Logger.info(`[CORS优化] 已为跨站JS添加crossorigin=anonymous: ${script.src}`); } // === JS脚本优化核心逻辑 === // 排除关键脚本 if (Utils.isCriticalResource(script, 'js')) return; // 已有async/defer不处理 if (script.hasAttribute('async') || script.hasAttribute('defer')) return; // 白名单不处理 if (CONFIG.LISTS.WHITELIST.some(domain => script.src.includes(domain))) return; // 默认加defer(更安全),如需更激进可加async script.setAttribute('defer', 'defer'); STATE.stats.optimizedScripts++; Logger.info(`[JS优化] 已为脚本添加defer: ${script.src}`); }); } // 优化样式 optimizeStyles() { const styles = document.querySelectorAll('link[rel="stylesheet"]'); Logger.info(`Found ${styles.length} style elements to optimize`); styles.forEach(style => { const cdnInfo = Utils.isCDNResource(style.href); if (cdnInfo.isCDN) { STATE.stats.cdnResources++; Logger.info(`[CDN识别] CSS: ${style.href} | provider: ${cdnInfo.provider} | type: ${cdnInfo.type}`); // === CDN资源预加载优化 === if (!Utils.isCriticalResource(style, 'css') && !document.querySelector(`link[rel="preload"][href="${style.href}"]`)) { const preload = document.createElement('link'); preload.rel = 'preload'; preload.as = 'style'; preload.href = style.href; preload.crossOrigin = 'anonymous'; document.head.appendChild(preload); STATE.stats.cdnResourcesPreloaded = (STATE.stats.cdnResourcesPreloaded || 0) + 1; STATE.stats.dynamicCDNOptimized = (STATE.stats.dynamicCDNOptimized || 0) + 1; Logger.info(`[CDN优化] 已为CDN CSS资源添加preload: ${style.href}`); } } // === 跨站资源CORS安全处理 === if (Utils.isCrossOriginResource(style.href) && !style.hasAttribute('crossorigin') && !Utils.isCriticalResource(style, 'css')) { style.setAttribute('crossorigin', 'anonymous'); STATE.stats.crossOriginResources++; Logger.info(`[CORS优化] 已为跨站CSS添加crossorigin=anonymous: ${style.href}`); } // === CSS优化核心逻辑 === // 排除关键CSS if (Utils.isCriticalResource(style, 'css')) return; // 已有media=print或preload不处理 if (style.media === 'print' || style.rel === 'preload') return; // 白名单不处理 if (CONFIG.LISTS.WHITELIST.some(domain => style.href.includes(domain))) return; // 优化:先media=print,onload后切回all(兼容性好) style.media = 'print'; style.onload = function() { this.media = 'all'; }; STATE.stats.optimizedStyles++; Logger.info(`[CSS优化] 已为样式添加media=print异步加载: ${style.href}`); }); } // 优化图片 optimizeImages() { const images = document.querySelectorAll('img'); Logger.info(`Found ${images.length} image elements to optimize`); images.forEach(img => { const cdnInfo = Utils.isCDNResource(img.src); if (cdnInfo.isCDN) { STATE.stats.cdnResources++; Logger.info(`[CDN识别] IMG: ${img.src} | provider: ${cdnInfo.provider} | type: ${cdnInfo.type}`); } // === 跨站资源CORS安全处理 === if (Utils.isCrossOriginResource(img.src) && !img.hasAttribute('crossorigin') && !Utils.isCriticalResource(img, 'img')) { img.setAttribute('crossorigin', 'anonymous'); STATE.stats.crossOriginResources++; Logger.info(`[CORS优化] 已为跨站IMG添加crossorigin=anonymous: ${img.src}`); } // === 图片懒加载优化核心逻辑 === // 排除关键图片 if (Utils.isCriticalResource(img, 'img')) return; // logo图片排除(常见logo类名/id) const alt = (img.alt || '').toLowerCase(); const cls = (img.className || '').toLowerCase(); const id = (img.id || '').toLowerCase(); if (alt.includes('logo') || cls.includes('logo') || id.includes('logo')) return; // 已有loading属性不处理 if (img.hasAttribute('loading')) return; // 白名单不处理 if (CONFIG.LISTS.WHITELIST.some(domain => img.src.includes(domain))) return; // 添加懒加载 img.setAttribute('loading', 'lazy'); STATE.stats.optimizedImages++; Logger.info(`[图片优化] 已为图片添加loading=lazy: ${img.src}`); }); } // 优化字体 optimizeFonts() { const fonts = document.querySelectorAll('link[rel="preload"][as="font"], link[rel="stylesheet"][href*="font"], link[rel="stylesheet"][href*="fonts"]'); Logger.info(`Found ${fonts.length} font elements to optimize`); fonts.forEach(font => { const href = font.href; const isPreload = font.rel === 'preload' && font.getAttribute('as') === 'font'; // === 跨站资源CORS安全处理 === if (Utils.isCrossOriginResource(href) && !font.hasAttribute('crossorigin') && !Utils.isCriticalResource(font, 'font')) { font.setAttribute('crossorigin', 'anonymous'); STATE.stats.crossOriginResources++; Logger.info(`[CORS优化] 已为跨站字体添加crossorigin=anonymous: ${href}`); } // CDN字体预加载 const cdnInfo = Utils.isCDNResource(href); if (cdnInfo.isCDN && !isPreload && !Utils.isCriticalResource(font, 'font') && !document.querySelector(`link[rel="preload"][href="${href}"]`)) { const preload = document.createElement('link'); preload.rel = 'preload'; preload.as = 'font'; preload.href = href; preload.crossOrigin = 'anonymous'; document.head.appendChild(preload); STATE.stats.cdnResourcesPreloaded = (STATE.stats.cdnResourcesPreloaded || 0) + 1; STATE.stats.dynamicCDNOptimized = (STATE.stats.dynamicCDNOptimized || 0) + 1; Logger.info(`[CDN优化] 已为CDN 字体资源添加preload: ${href}`); } // 只处理未preload的关键字体 if (isPreload) return; if (!href || href.endsWith('.css')) return; // 跳过样式表 if (CONFIG.LISTS.WHITELIST.some(domain => href.includes(domain))) return; // 动态插入preload标签 const preload = document.createElement('link'); preload.rel = 'preload'; preload.as = 'font'; preload.href = href; preload.crossOrigin = 'anonymous'; document.head.appendChild(preload); STATE.stats.optimizedFonts++; Logger.info(`[字体优化] 已为字体资源添加preload: ${href}`); }); } // 开始动态监控 startDynamicMonitoring() { if (!CONFIG.FEATURES.DYNAMIC_MONITORING) return; Logger.info('Starting dynamic resource monitoring'); this.observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList') { mutation.addedNodes.forEach((node) => { if (node.nodeType === Node.ELEMENT_NODE) { this.handleNewElementRecursive(node); } }); } }); }); this.observer.observe(document, { childList: true, subtree: true }); } // 递归处理新元素及其子元素 handleNewElementRecursive(element) { this.handleNewElement(element); if (element.children && element.children.length > 0) { Array.from(element.children).forEach(child => this.handleNewElementRecursive(child)); } STATE.stats.dynamicOptimized = (STATE.stats.dynamicOptimized || 0) + 1; } // 处理新添加的元素 handleNewElement(element) { // 动态CDN资源预加载和CORS优化 if (element.tagName === 'SCRIPT' && element.src) { const cdnInfo = Utils.isCDNResource(element.src); if (cdnInfo.isCDN) { // CDN预加载 if (!Utils.isCriticalResource(element, 'js') && !document.querySelector(`link[rel="preload"][href="${element.src}"]`)) { const preload = document.createElement('link'); preload.rel = 'preload'; preload.as = 'script'; preload.href = element.src; preload.crossOrigin = 'anonymous'; document.head.appendChild(preload); STATE.stats.cdnResourcesPreloaded = (STATE.stats.cdnResourcesPreloaded || 0) + 1; STATE.stats.dynamicCDNOptimized = (STATE.stats.dynamicCDNOptimized || 0) + 1; Logger.info(`[CDN优化-动态] 已为动态CDN JS资源添加preload: ${element.src}`); } } if (Utils.isCrossOriginResource(element.src) && !element.hasAttribute('crossorigin') && !Utils.isCriticalResource(element, 'js')) { element.setAttribute('crossorigin', 'anonymous'); STATE.stats.crossOriginResources++; Logger.info(`[CORS优化-动态] 已为动态跨站JS添加crossorigin=anonymous: ${element.src}`); } // === JS脚本优化核心逻辑 === // 排除关键脚本 if (Utils.isCriticalResource(element, 'js')) return; if (element.hasAttribute('async') || element.hasAttribute('defer')) return; if (CONFIG.LISTS.WHITELIST.some(domain => element.src.includes(domain))) return; element.setAttribute('defer', 'defer'); STATE.stats.optimizedScripts++; Logger.info(`[JS优化-动态] 已为脚本添加defer: ${element.src}`); } if (element.tagName === 'LINK' && element.rel === 'stylesheet' && element.href) { const cdnInfo = Utils.isCDNResource(element.href); if (cdnInfo.isCDN) { // CDN预加载 if (!Utils.isCriticalResource(element, 'css') && !document.querySelector(`link[rel="preload"][href="${element.href}"]`)) { const preload = document.createElement('link'); preload.rel = 'preload'; preload.as = 'style'; preload.href = element.href; preload.crossOrigin = 'anonymous'; document.head.appendChild(preload); STATE.stats.cdnResourcesPreloaded = (STATE.stats.cdnResourcesPreloaded || 0) + 1; STATE.stats.dynamicCDNOptimized = (STATE.stats.dynamicCDNOptimized || 0) + 1; Logger.info(`[CDN优化-动态] 已为动态CDN CSS资源添加preload: ${element.href}`); } } if (Utils.isCrossOriginResource(element.href) && !element.hasAttribute('crossorigin') && !Utils.isCriticalResource(element, 'css')) { element.setAttribute('crossorigin', 'anonymous'); STATE.stats.crossOriginResources++; Logger.info(`[CORS优化-动态] 已为动态跨站CSS添加crossorigin=anonymous: ${element.href}`); } // === CSS优化核心逻辑 === // 排除关键CSS if (Utils.isCriticalResource(element, 'css')) return; if (element.media === 'print' || element.rel === 'preload') return; if (CONFIG.LISTS.WHITELIST.some(domain => element.href.includes(domain))) return; element.media = 'print'; element.onload = function() { this.media = 'all'; }; STATE.stats.optimizedStyles++; Logger.info(`[CSS优化-动态] 已为样式添加media=print异步加载: ${element.href}`); } if (element.tagName === 'LINK' && (element.rel === 'stylesheet' || element.rel === 'preload') && element.href && (element.href.includes('font') || element.href.includes('fonts'))) { const href = element.href; const isPreload = element.rel === 'preload' && element.getAttribute('as') === 'font'; const cdnInfo = Utils.isCDNResource(href); if (cdnInfo.isCDN && !isPreload && !Utils.isCriticalResource(element, 'font') && !document.querySelector(`link[rel="preload"][href="${href}"]`)) { const preload = document.createElement('link'); preload.rel = 'preload'; preload.as = 'font'; preload.href = href; preload.crossOrigin = 'anonymous'; document.head.appendChild(preload); STATE.stats.cdnResourcesPreloaded = (STATE.stats.cdnResourcesPreloaded || 0) + 1; STATE.stats.dynamicCDNOptimized = (STATE.stats.dynamicCDNOptimized || 0) + 1; Logger.info(`[CDN优化-动态] 已为动态CDN 字体资源添加preload: ${href}`); } if (Utils.isCrossOriginResource(href) && !element.hasAttribute('crossorigin') && !Utils.isCriticalResource(element, 'font')) { element.setAttribute('crossorigin', 'anonymous'); STATE.stats.crossOriginResources++; Logger.info(`[CORS优化-动态] 已为动态跨站字体添加crossorigin=anonymous: ${href}`); } // === 字体优化核心逻辑 === const isPreload2 = element.rel === 'preload' && element.getAttribute('as') === 'font'; if (isPreload2) return; if (!href || href.endsWith('.css')) return; if (CONFIG.LISTS.WHITELIST.some(domain => href.includes(domain))) return; const preload = document.createElement('link'); preload.rel = 'preload'; preload.as = 'font'; preload.href = href; preload.crossOrigin = 'anonymous'; document.head.appendChild(preload); STATE.stats.optimizedFonts++; Logger.info(`[字体优化-动态] 已为字体资源添加preload: ${href}`); } if (element.tagName === 'IMG' && element.src) { const cdnInfo = Utils.isCDNResource(element.src); if (cdnInfo.isCDN) { STATE.stats.cdnResources++; Logger.info(`[CDN识别-动态] IMG: ${element.src} | provider: ${cdnInfo.provider} | type: ${cdnInfo.type}`); } // === 动态图片懒加载优化 === if (Utils.isCriticalResource(element, 'img')) return; const alt = (element.alt || '').toLowerCase(); const cls = (element.className || '').toLowerCase(); const id = (element.id || '').toLowerCase(); if (alt.includes('logo') || cls.includes('logo') || id.includes('logo')) return; if (element.hasAttribute('loading')) return; if (CONFIG.LISTS.WHITELIST.some(domain => element.src.includes(domain))) return; element.setAttribute('loading', 'lazy'); STATE.stats.optimizedImages++; Logger.info(`[图片优化-动态] 已为图片添加loading=lazy: ${element.src}`); } Logger.debug('New element detected', element.tagName); } // 获取统计信息 getStats() { return { ...STATE.stats, uptime: performance.now() - STATE.performance.startTime }; } // 重置统计信息 resetStats() { STATE.stats = { optimizedScripts: 0, optimizedStyles: 0, optimizedImages: 0, optimizedFonts: 0, cdnResources: 0, crossOriginResources: 0, errors: 0, warnings: 0, criticalProtected: 0, cdnResourcesPreloaded: 0, spaOptimized: 0, dynamicCDNOptimized: 0 }; } // 启用/禁用优化器 setEnabled(enabled) { STATE.enabled = enabled; Logger.info(`Resource optimizer ${enabled ? 'enabled' : 'disabled'}`); } // 清理资源 destroy() { if (this.observer) { this.observer.disconnect(); this.observer = null; } Logger.info('Resource Optimizer Controller destroyed'); } // 回退所有优化 rollbackAll() { let count = 0; for (const [el] of STATE.originalStates.scripts) { if (el && typeof el.tagName === 'string') { if (typeof this.optimizers?.[0]?.rollback === 'function') { if (this.optimizers[0].rollback(el)) count++; } } } for (const [el] of STATE.originalStates.styles) { if (el && typeof el.tagName === 'string') { if (typeof this.optimizers?.[0]?.rollback === 'function') { if (this.optimizers[0].rollback(el)) count++; } } } for (const [el] of STATE.originalStates.fonts) { if (el && typeof el.tagName === 'string') { if (typeof this.optimizers?.[0]?.rollback === 'function') { if (this.optimizers[0].rollback(el)) count++; } } } for (const [el] of STATE.originalStates.images) { if (el && typeof el.tagName === 'string') { if (typeof this.optimizers?.[0]?.rollback === 'function') { if (this.optimizers[0].rollback(el)) count++; } } } Logger.info(`Rollback all: ${count} elements restored`); return count; } } // 全局实例 let controller = null; // 初始化函数 function init() { try { Logger.info('Starting Web Resource Priority Optimizer'); // 创建控制器 controller = new ResourceOptimizerController(); // 初始化控制器 controller.init(); // 暴露到全局(用于调试) if (CONFIG.DEBUG) { window.ResourceOptimizer = { controller: controller, config: CONFIG, state: STATE, logger: Logger, utils: Utils }; } Logger.info('Web Resource Priority Optimizer started successfully'); } catch (error) { Logger.error('Failed to initialize Web Resource Priority Optimizer', error); } } // 页面加载完成后初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } // 导出到全局(用于后续任务) window.WebResourceOptimizer = { init: init, controller: controller, config: CONFIG, state: STATE, logger: Logger, utils: Utils, ResourceOptimizer: ResourceOptimizer, ResourceOptimizerController: ResourceOptimizerController }; // SPA路由兼容 function setupSPARouterCompatibility(controller) { let lastUrl = location.href; let spaOptimizeCount = 0; function onRouteChange() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(() => { if (controller) { controller.optimizeExistingResources(); spaOptimizeCount++; STATE.stats.spaOptimized = (STATE.stats.spaOptimized || 0) + 1; Logger.info(`[SPA兼容] 路由变化后已重新优化资源,第${spaOptimizeCount}次`); } }, 100); } } // 劫持pushState/replaceState const rawPush = history.pushState; const rawReplace = history.replaceState; history.pushState = function(...args) { rawPush.apply(this, args); onRouteChange(); }; history.replaceState = function(...args) { rawReplace.apply(this, args); onRouteChange(); }; window.addEventListener('popstate', onRouteChange); } // 资源加载失败检测与回退 function attachResourceErrorHandler(element) { if (!element || !element.tagName) return; if (element._wro_errorHandlerAttached) return; element._wro_errorHandlerAttached = true; element.addEventListener('error', function() { Logger.error(`资源加载失败: ${element.tagName} ${element.src || element.href}`); // 单资源多次失败可回退该资源 if (window.WebResourceOptimizer && window.WebResourceOptimizer.controller) { window.WebResourceOptimizer.controller.optimizers?.[0]?.rollback?.(element); } }, true); } // 兼容性检测与降级 const COMPAT = { Map: typeof Map !== 'undefined', Blob: typeof Blob !== 'undefined', URL: typeof URL !== 'undefined' && typeof URL.createObjectURL === 'function', addEventListener: typeof window.addEventListener === 'function', defineProperty: typeof Object.defineProperty === 'function', GM_log: typeof GM_log !== 'undefined', GM_registerMenuCommand: typeof GM_registerMenuCommand !== 'undefined', MutationObserver: typeof MutationObserver !== 'undefined', performance: typeof performance !== 'undefined' && typeof performance.now === 'function' }; const COMPAT_WARNINGS = []; if (!COMPAT.Map) COMPAT_WARNINGS.push('Map 不支持'); if (!COMPAT.Blob) COMPAT_WARNINGS.push('Blob 不支持,日志导出不可用'); if (!COMPAT.URL) COMPAT_WARNINGS.push('URL.createObjectURL 不支持,日志导出不可用'); if (!COMPAT.addEventListener) COMPAT_WARNINGS.push('addEventListener 不支持,无法监听资源错误'); if (!COMPAT.defineProperty) COMPAT_WARNINGS.push('Object.defineProperty 不支持,部分全局开关不可用'); if (!COMPAT.MutationObserver) COMPAT_WARNINGS.push('MutationObserver 不支持,动态优化不可用'); if (!COMPAT.performance) COMPAT_WARNINGS.push('performance.now 不支持,运行时间不可用'); })();