您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
全面的网页性能优化方案- 懒加载/预加载/预连接/布局优化
// ==UserScript== // @name Ultimate Web Optimizer // @namespace https://gf.qytechs.cn/zh-CN/users/1474228-moyu001 // @version 2.1 // @description 全面的网页性能优化方案- 懒加载/预加载/预连接/布局优化 // @author moyu001 // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @grant GM_log // @license MIT // @run-at document-start // ==/UserScript== (function() { 'use strict'; // ======================== // 工具函数 - 提前定义 // ======================== /** * 防抖函数 * @param {Function} fn 要防抖的函数 * @param {number} delay 延迟时间 * @returns {Function} 防抖后的函数 */ function debounce(fn, delay) { let timer = null; return function(...args) { clearTimeout(timer); timer = setTimeout(() => fn.apply(this, args), delay); }; } /** * 节流函数 * @param {Function} func 要节流的函数 * @param {number} limit 限制时间 * @returns {Function} 节流后的函数 */ function throttle(func, limit) { let inThrottle; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } /** * 安全的 URL 解析 * @param {string} url URL 字符串 * @param {string} base 基准 URL * @returns {URL|null} URL 对象或 null */ function safeParseURL(url, base) { try { return new URL(url, base); } catch { return null; } } /** * 检查元素是否可见 * @param {Element} element 要检查的元素 * @returns {boolean} 是否可见 */ function isElementVisible(element) { if (!element) return false; const style = window.getComputedStyle(element); return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0'; } /** * 深度合并对象 * @param {Object} target 目标对象 * @param {Object} source 源对象 * @returns {Object} 合并后的对象 */ function deepMerge(target, source) { const result = { ...target }; for (const key in source) { if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { result[key] = deepMerge(result[key] || {}, source[key]); } else { result[key] = source[key]; } } return result; } /** * 检查是否为 HTML 图片元素 - 增强类型检查 * @param {Node} node DOM 节点 * @returns {boolean} 是否为图片元素 */ function isImageElement(node) { return node instanceof HTMLImageElement; } /** * 延迟函数 * @param {number} ms 延迟毫秒数 * @returns {Promise} Promise 对象 */ function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } /** * 简单的 LRU 缓存实现 */ class LRUCache { constructor(maxSize = 100) { this.maxSize = maxSize; this.cache = new Map(); } get(key) { if (this.cache.has(key)) { const value = this.cache.get(key); this.cache.delete(key); this.cache.set(key, value); return value; } return null; } set(key, value) { if (this.cache.has(key)) { this.cache.delete(key); } else if (this.cache.size >= this.maxSize) { const firstKey = this.cache.keys().next().value; this.cache.delete(firstKey); } this.cache.set(key, value); } has(key) { return this.cache.has(key); } clear() { this.cache.clear(); } get size() { return this.cache.size; } } /** * 重试操作工具类 - 新增错误重试机制 */ class RetryableOperation { /** * 执行带重试的操作 * @param {Function} operation 要执行的操作 * @param {number} maxRetries 最大重试次数 * @param {number} baseDelay 基础延迟时间 * @returns {Promise} 操作结果 */ static async executeWithRetry(operation, maxRetries = 3, baseDelay = 1000) { for (let i = 0; i < maxRetries; i++) { try { return await operation(); } catch (e) { if (i === maxRetries - 1) throw e; await delay(baseDelay * (i + 1)); } } } } /** * 性能监控器 */ class PerformanceMonitor { constructor(debug = false) { this.debug = debug; this.metrics = new Map(); this.counters = new Map(); } start(name) { if (this.debug) { this.metrics.set(name, performance.now()); } } end(name) { if (this.debug && this.metrics.has(name)) { const duration = performance.now() - this.metrics.get(name); console.log(`[性能] ${name}: ${duration.toFixed(2)}ms`); this.metrics.delete(name); return duration; } return 0; } count(name) { if (this.debug) { this.counters.set(name, (this.counters.get(name) || 0) + 1); } } getCounter(name) { return this.counters.get(name) || 0; } profile(name, fn) { if (!this.debug) return fn(); const start = performance.now(); const result = fn(); const end = performance.now(); console.log(`[性能] ${name}: ${(end - start).toFixed(2)}ms`); return result; } log(message, ...args) { if (this.debug) { console.log(`[优化器] ${message}`, ...args); } } warn(message, ...args) { if (this.debug) { console.warn(`[优化器] ${message}`, ...args); } } error(message, ...args) { if (this.debug) { console.error(`[优化器] ${message}`, ...args); } } } // ======================== // 配置管理系统 // ======================== /** * 配置管理器 - 使用深度合并 */ class ConfigManager { constructor() { this.defaultConfig = { debug: false, // 全局黑名单 globalBlacklist: [ // 可以添加需要完全跳过优化的域名 ], // 懒加载配置 lazyLoad: { enabled: true, minSize: 100, rootMargin: '200px', threshold: 0.01, skipHidden: true, batchSize: 32, blacklist: [] }, // 预连接配置 preconnect: { enabled: true, maxConnections: 5, whitelist: [ // CDN 和字体服务 'fonts.gstatic.com', 'fonts.googleapis.com', 'fonts.googleapis.cn', 'fonts.loli.net', // 常用 CDN 'cdnjs.cloudflare.com', 'cdn.jsdelivr.net', 'unpkg.com', 'cdn.bootcdn.net', 'cdn.bootcss.com', 'libs.baidu.com', 'cdn.staticfile.org', // 其他常用服务 'ajax.googleapis.com', 'code.jquery.com', 'maxcdn.bootstrapcdn.com', 'kit.fontawesome.com', 'lf3-cdn-tos.bytecdntp.com', 'unpkg.zhimg.com', 'npm.elemecdn.com', 'g.alicdn.com' ], blacklist: [] }, // 预加载配置 preload: { enabled: true, maxPreloads: 5, types: ['css', 'js', 'woff2', 'woff'], fetchTimeout: 5000, retryAttempts: 3, blacklist: [] }, // 布局稳定性配置 layout: { enabled: true, stableImages: true, stableIframes: true, blacklist: [] } }; this.config = this.loadConfig(); this.validateConfig(); } loadConfig() { try { const saved = GM_getValue('optimizer_config_v2', null); if (saved) { // 使用深度合并替代浅合并 return deepMerge(this.defaultConfig, JSON.parse(saved)); } } catch (e) { console.warn('[配置] 加载用户配置失败,使用默认配置', e); } return deepMerge({}, this.defaultConfig); } saveConfig() { try { GM_setValue('optimizer_config_v2', JSON.stringify(this.config)); } catch (e) { console.warn('[配置] 保存配置失败', e); } } validateConfig() { // 基本类型验证 if (typeof this.config.debug !== 'boolean') { this.config.debug = this.defaultConfig.debug; } // 确保数组类型配置正确 ['globalBlacklist'].forEach(key => { if (!Array.isArray(this.config[key])) { this.config[key] = [...this.defaultConfig[key]]; } }); // 验证子配置 ['lazyLoad', 'preconnect', 'preload', 'layout'].forEach(module => { if (!this.config[module] || typeof this.config[module] !== 'object') { this.config[module] = deepMerge({}, this.defaultConfig[module]); } }); } get(path) { const keys = path.split('.'); let value = this.config; for (const key of keys) { value = value?.[key]; if (value === undefined) break; } return value; } set(path, value) { const keys = path.split('.'); const lastKey = keys.pop(); let target = this.config; for (const key of keys) { if (!target[key] || typeof target[key] !== 'object') { target[key] = {}; } target = target[key]; } target[lastKey] = value; this.saveConfig(); } isBlacklisted(hostname, module = null) { // 检查全局黑名单 if (this.config.globalBlacklist.some(domain => hostname.includes(domain))) { return true; } // 检查模块特定黑名单 if (module && this.config[module]?.blacklist) { return this.config[module].blacklist.some(domain => hostname.includes(domain)); } return false; } } // ======================== // 核心优化模块 // ======================== /** * 懒加载管理器 - 增强类型检查 */ class LazyLoadManager { constructor(config, monitor) { this.config = config; this.monitor = monitor; this.observer = null; this.mutationObserver = null; this.processedImages = new Set(); this.pendingImages = []; this.batchScheduled = false; this.processedElements = new WeakSet(); // 避免重复处理 } init() { if (!this.config.get('lazyLoad.enabled')) { this.monitor.log('懒加载功能已禁用'); return; } if (this.config.isBlacklisted(location.hostname, 'lazyLoad')) { this.monitor.log('当前站点在懒加载黑名单中'); return; } this.monitor.start('lazyLoad-init'); this.setupIntersectionObserver(); this.processExistingImages(); this.setupMutationObserver(); this.monitor.end('lazyLoad-init'); } setupIntersectionObserver() { if (!('IntersectionObserver' in window)) { this.monitor.warn('浏览器不支持 IntersectionObserver,使用兼容模式'); this.setupFallbackMode(); return; } this.observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { this.restoreImage(entry.target); this.observer.unobserve(entry.target); this.monitor.count('lazy-loaded-images'); } }); }, { rootMargin: this.config.get('lazyLoad.rootMargin'), threshold: this.config.get('lazyLoad.threshold') }); } setupFallbackMode() { const checkVisible = throttle(() => { const images = document.querySelectorAll('img[data-lazy-src]'); const margin = parseInt(this.config.get('lazyLoad.rootMargin')) || 200; images.forEach(img => { const rect = img.getBoundingClientRect(); if (rect.top < window.innerHeight + margin) { this.restoreImage(img); } }); }, 200); window.addEventListener('scroll', checkVisible, { passive: true }); window.addEventListener('resize', checkVisible, { passive: true }); checkVisible(); } isLazyCandidate(img) { // 基本检查 - 使用更严格的类型检查 if (!isImageElement(img)) return false; if (this.processedElements.has(img)) return false; if (img.hasAttribute('data-lazy-processed')) return false; if (img.loading === 'eager') return false; if (img.complete && img.src) return false; if (img.src && img.src.startsWith('data:')) return false; // 跳过已有懒加载的图片 if (img.hasAttribute('data-src') || img.hasAttribute('data-srcset')) return false; // 尺寸检查 const minSize = this.config.get('lazyLoad.minSize'); const rect = img.getBoundingClientRect(); if (rect.width < minSize || rect.height < minSize) return false; // 可见性检查 if (this.config.get('lazyLoad.skipHidden') && !isElementVisible(img)) { return false; } return true; } processImage(img) { if (!this.isLazyCandidate(img)) return false; // 标记为已处理 this.processedElements.add(img); img.setAttribute('data-lazy-processed', 'true'); // 设置原生懒加载(如果支持) if ('loading' in HTMLImageElement.prototype) { img.loading = 'lazy'; } // 保存原始 src if (img.src) { img.setAttribute('data-lazy-src', img.src); img.removeAttribute('src'); } if (img.srcset) { img.setAttribute('data-lazy-srcset', img.srcset); img.removeAttribute('srcset'); } // 添加到观察者 if (this.observer) { this.observer.observe(img); } this.processedImages.add(img); this.monitor.count('processed-images'); return true; } restoreImage(img) { const src = img.getAttribute('data-lazy-src'); const srcset = img.getAttribute('data-lazy-srcset'); if (src) { img.src = src; img.removeAttribute('data-lazy-src'); } if (srcset) { img.srcset = srcset; img.removeAttribute('data-lazy-srcset'); } this.processedImages.delete(img); } batchProcess(images) { const batchSize = this.config.get('lazyLoad.batchSize'); let processed = 0; const processBatch = () => { const end = Math.min(processed + batchSize, images.length); for (let i = processed; i < end; i++) { this.processImage(images[i]); } processed = end; if (processed < images.length) { (window.requestIdleCallback || window.requestAnimationFrame)(processBatch); } else { this.monitor.log(`懒加载处理完成,共处理 ${processed} 张图片`); } }; processBatch(); } processExistingImages() { const images = Array.from(document.querySelectorAll('img')); this.monitor.log(`发现 ${images.length} 张图片,开始批量处理`); this.batchProcess(images); } // 改进的批处理调度 - 更好的并发控制 scheduleBatchProcess() { if (this.batchScheduled || this.pendingImages.length === 0) return; this.batchScheduled = true; (window.requestIdleCallback || window.requestAnimationFrame)(() => { const images = [...this.pendingImages]; this.pendingImages = []; this.batchScheduled = false; let processedCount = 0; images.forEach(img => { if (this.processImage(img)) { processedCount++; } }); if (processedCount > 0) { this.monitor.log(`动态处理 ${processedCount} 张新图片`); } }); } setupMutationObserver() { let pendingMutations = []; let processingScheduled = false; const processMutations = () => { const mutations = [...pendingMutations]; pendingMutations = []; processingScheduled = false; mutations.forEach(mutation => { // 处理新增节点 mutation.addedNodes.forEach(node => { if (isImageElement(node)) { this.pendingImages.push(node); } else if (node.querySelectorAll) { const images = node.querySelectorAll('img'); this.pendingImages.push(...Array.from(images)); } }); // 清理移除的节点 mutation.removedNodes.forEach(node => { if (isImageElement(node) && this.observer) { this.observer.unobserve(node); this.processedImages.delete(node); this.processedElements.delete && this.processedElements.delete(node); } }); }); this.scheduleBatchProcess(); }; this.mutationObserver = new MutationObserver((mutations) => { pendingMutations.push(...mutations); if (!processingScheduled) { processingScheduled = true; (window.requestIdleCallback || window.requestAnimationFrame)(processMutations); } }); this.mutationObserver.observe(document.body, { childList: true, subtree: true }); } destroy() { if (this.observer) { this.observer.disconnect(); this.observer = null; } if (this.mutationObserver) { this.mutationObserver.disconnect(); this.mutationObserver = null; } // 恢复所有处理过的图片 this.processedImages.forEach(img => this.restoreImage(img)); this.processedImages.clear(); } } /** * 预加载管理器 - 改进异步处理 */ class PreloadManager { constructor(config, monitor) { this.config = config; this.monitor = monitor; this.preloaded = new LRUCache(this.config.get('preload.maxPreloads')); this.cssCache = new LRUCache(50); this.mutationObserver = null; } async init() { if (!this.config.get('preload.enabled')) { this.monitor.log('预加载功能已禁用'); return; } if (this.config.isBlacklisted(location.hostname, 'preload')) { this.monitor.log('当前站点在预加载黑名单中'); return; } this.monitor.start('preload-init'); await this.scanExistingResources(); // 改为异步 this.setupMutationObserver(); this.monitor.end('preload-init'); } getResourceType(url) { const ext = url.split('.').pop()?.toLowerCase(); const types = this.config.get('preload.types'); if (!types.includes(ext)) return null; switch (ext) { case 'css': return 'style'; case 'js': return 'script'; case 'woff': case 'woff2': case 'ttf': case 'otf': return 'font'; default: return null; } } doPreload(url, asType) { if (this.preloaded.has(url) || this.preloaded.size >= this.config.get('preload.maxPreloads')) { return false; } try { const link = document.createElement('link'); link.rel = 'preload'; link.as = asType; link.href = url; if (asType === 'font') { link.crossOrigin = 'anonymous'; // 设置正确的 MIME 类型 if (url.includes('.woff2')) link.type = 'font/woff2'; else if (url.includes('.woff')) link.type = 'font/woff'; else if (url.includes('.ttf')) link.type = 'font/ttf'; else if (url.includes('.otf')) link.type = 'font/otf'; } document.head.appendChild(link); this.preloaded.set(url, true); this.monitor.log(`预加载 ${asType}: ${url}`); this.monitor.count('preloaded-resources'); return true; } catch (e) { this.monitor.warn(`预加载失败: ${url}`, e); return false; } } async extractFontsFromCSS(cssUrl) { if (this.cssCache.has(cssUrl)) { return this.cssCache.get(cssUrl); } const operation = async () => { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.config.get('preload.fetchTimeout')); try { const response = await fetch(cssUrl, { signal: controller.signal, mode: 'cors', credentials: 'omit' }); clearTimeout(timeoutId); if (!response.ok) throw new Error(`HTTP ${response.status}`); const text = await response.text(); const fontUrls = []; const fontRegex = /url\(["']?([^")']+\.(woff2?|ttf|otf))["']?\)/gi; let match; while ((match = fontRegex.exec(text)) !== null) { const fontUrl = safeParseURL(match[1], cssUrl); if (fontUrl) { fontUrls.push(fontUrl.href); } } this.cssCache.set(cssUrl, fontUrls); return fontUrls; } finally { clearTimeout(timeoutId); } }; try { // 使用重试机制 return await RetryableOperation.executeWithRetry( operation, this.config.get('preload.retryAttempts') ); } catch (e) { this.monitor.warn(`提取字体失败: ${cssUrl}`, e.message); this.cssCache.set(cssUrl, []); return []; } } // 改进的异步资源扫描 - 更好的并发控制 async scanExistingResources() { // 处理 CSS 文件 const cssLinks = Array.from(document.querySelectorAll('link[rel="stylesheet"][href]')); const jsScripts = Array.from(document.querySelectorAll('script[src]')); // 处理 CSS 文件的 Promise 数组 const cssPromises = cssLinks.map(async link => { const cssUrl = link.href; const asType = this.getResourceType(cssUrl); if (asType === 'style') { this.doPreload(cssUrl, asType); // 异步提取和预加载字体 try { const fontUrls = await this.extractFontsFromCSS(cssUrl); fontUrls.forEach(fontUrl => { const fontType = this.getResourceType(fontUrl); if (fontType === 'font') { this.doPreload(fontUrl, fontType); } }); } catch (e) { // 忽略字体提取错误,不影响主流程 this.monitor.warn(`CSS处理失败: ${cssUrl}`, e); } } }); // 处理 JS 文件 jsScripts.forEach(script => { const asType = this.getResourceType(script.src); if (asType === 'script') { this.doPreload(script.src, asType); } }); // 等待所有 CSS 处理完成,但不阻塞初始化 try { await Promise.allSettled(cssPromises); this.monitor.log(`资源扫描完成,处理了 ${cssLinks.length} 个CSS文件和 ${jsScripts.length} 个JS文件`); } catch (e) { this.monitor.warn('资源扫描过程中出现错误', e); } } setupMutationObserver() { this.mutationObserver = new MutationObserver(debounce(async (mutations) => { const newCSSLinks = []; const newJSScripts = []; mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.tagName === 'LINK' && node.rel === 'stylesheet' && node.href) { newCSSLinks.push(node); } else if (node.tagName === 'SCRIPT' && node.src) { newJSScripts.push(node); } }); }); // 异步处理新添加的资源 if (newCSSLinks.length > 0 || newJSScripts.length > 0) { const promises = newCSSLinks.map(async node => { const asType = this.getResourceType(node.href); if (asType === 'style') { this.doPreload(node.href, asType); // 异步处理字体 try { const fontUrls = await this.extractFontsFromCSS(node.href); fontUrls.forEach(fontUrl => { const fontType = this.getResourceType(fontUrl); if (fontType === 'font') { this.doPreload(fontUrl, fontType); } }); } catch (e) { // 忽略错误 } } }); newJSScripts.forEach(node => { const asType = this.getResourceType(node.src); if (asType === 'script') { this.doPreload(node.src, asType); } }); // 不等待 Promise 完成,避免阻塞 Promise.allSettled(promises).then(() => { this.monitor.log(`动态处理了 ${newCSSLinks.length} 个CSS和 ${newJSScripts.length} 个JS`); }); } }, 200)); this.mutationObserver.observe(document.body, { childList: true, subtree: true }); } destroy() { if (this.mutationObserver) { this.mutationObserver.disconnect(); this.mutationObserver = null; } this.preloaded.clear(); this.cssCache.clear(); } } // 其他管理器类保持不变,仅引用已改进的配置和监控器 class PreconnectManager { constructor(config, monitor) { this.config = config; this.monitor = monitor; this.connected = new LRUCache(this.config.get('preconnect.maxConnections')); this.mutationObserver = null; } init() { if (!this.config.get('preconnect.enabled')) { this.monitor.log('预连接功能已禁用'); return; } if (this.config.isBlacklisted(location.hostname, 'preconnect')) { this.monitor.log('当前站点在预连接黑名单中'); return; } this.monitor.start('preconnect-init'); this.scanExistingResources(); this.setupMutationObserver(); this.monitor.end('preconnect-init'); } shouldPreconnect(hostname) { if (!hostname || hostname === location.hostname) return false; if (this.connected.has(hostname)) return false; const whitelist = this.config.get('preconnect.whitelist'); return whitelist.some(domain => hostname.endsWith(domain)); } doPreconnect(hostname) { if (!this.shouldPreconnect(hostname)) return false; try { const link = document.createElement('link'); link.rel = 'preconnect'; link.href = `https://${hostname}`; link.crossOrigin = 'anonymous'; document.head.appendChild(link); this.connected.set(hostname, true); this.monitor.log(`预连接: ${hostname}`); this.monitor.count('preconnected-domains'); return true; } catch (e) { this.monitor.warn(`预连接失败: ${hostname}`, e); return false; } } extractHostnames(elements) { const hostnames = new Set(); elements.forEach(el => { const url = safeParseURL(el.src || el.href); if (url && url.hostname !== location.hostname) { hostnames.add(url.hostname); } }); return Array.from(hostnames); } scanExistingResources() { const selectors = [ 'script[src]', 'link[href]', 'img[src]', 'audio[src]', 'video[src]', 'source[src]' ]; const elements = document.querySelectorAll(selectors.join(',')); const hostnames = this.extractHostnames(elements); let connected = 0; hostnames.forEach(hostname => { if (this.doPreconnect(hostname)) { connected++; } }); this.monitor.log(`扫描完成,预连接 ${connected} 个域名`); } setupMutationObserver() { this.mutationObserver = new MutationObserver(debounce((mutations) => { const newElements = []; mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.src || node.href) { newElements.push(node); } else if (node.querySelectorAll) { const elements = node.querySelectorAll('script[src], link[href], img[src]'); newElements.push(...elements); } }); }); if (newElements.length > 0) { const hostnames = this.extractHostnames(newElements); hostnames.forEach(hostname => this.doPreconnect(hostname)); } }, 200)); this.mutationObserver.observe(document.body, { childList: true, subtree: true }); } destroy() { if (this.mutationObserver) { this.mutationObserver.disconnect(); this.mutationObserver = null; } this.connected.clear(); } } class LayoutStabilizer { constructor(config, monitor) { this.config = config; this.monitor = monitor; this.injectedStyle = null; } init() { if (!this.config.get('layout.enabled')) { this.monitor.log('布局优化功能已禁用'); return; } if (this.config.isBlacklisted(location.hostname, 'layout')) { this.monitor.log('当前站点在布局优化黑名单中'); return; } this.monitor.start('layout-init'); this.injectStabilizationStyles(); this.monitor.end('layout-init'); } generateCSS() { const styles = []; if (this.config.get('layout.stableImages')) { styles.push(` /* 图片布局稳定性优化 */ img:not([width]):not([height]):not([style*="width"]):not([style*="height"]) { min-height: 1px; // max-width: 100%; // height: auto; } /* 现代浏览器的 aspect-ratio 支持 */ @supports (aspect-ratio: 1/1) { img[width][height]:not([style*="aspect-ratio"]) { aspect-ratio: attr(width) / attr(height); } } `); } if (this.config.get('layout.stableIframes')) { styles.push(` /* iframe 布局稳定性优化 */ iframe:not([width]):not([height]):not([style*="width"]):not([style*="height"]) { // width: 100%; // height: auto; min-height: 1px; } @supports (aspect-ratio: 16/9) { iframe:not([style*="aspect-ratio"]) { aspect-ratio: 16/9; } } `); } return styles.join('\n'); } injectStabilizationStyles() { const css = this.generateCSS().trim(); if (!css) return; try { this.injectedStyle = document.createElement('style'); this.injectedStyle.setAttribute('data-optimizer', 'layout-stabilizer'); this.injectedStyle.textContent = css; // 优先插入到 head,如果不存在则插入到 document const target = document.head || document.documentElement; target.appendChild(this.injectedStyle); this.monitor.log('布局稳定性样式已注入'); } catch (e) { this.monitor.warn('注入布局样式失败', e); } } destroy() { if (this.injectedStyle && this.injectedStyle.parentNode) { this.injectedStyle.parentNode.removeChild(this.injectedStyle); this.injectedStyle = null; this.monitor.log('布局样式已移除'); } } } // ======================== // 主优化器 // ======================== /** * 主优化器类 - v2.0 */ class WebOptimizer { constructor() { this.config = new ConfigManager(); this.monitor = new PerformanceMonitor(this.config.get('debug')); // 优化模块 this.modules = { lazyLoad: new LazyLoadManager(this.config, this.monitor), preconnect: new PreconnectManager(this.config, this.monitor), preload: new PreloadManager(this.config, this.monitor), layout: new LayoutStabilizer(this.config, this.monitor) }; this.initialized = false; this.cleanupTasks = []; } async init() { if (this.initialized) return; this.monitor.start('total-init'); this.monitor.log('Web Optimizer Enhanced v2.0 开始初始化'); // 检查全局黑名单 if (this.config.isBlacklisted(location.hostname)) { this.monitor.log('当前站点在全局黑名单中,跳过所有优化'); return; } try { // 等待 DOM 基本可用 if (document.readyState === 'loading') { await new Promise(resolve => { document.addEventListener('DOMContentLoaded', resolve, { once: true }); }); } // 初始化各个模块 - 改进的错误隔离 const initPromises = Object.entries(this.modules).map(async ([name, module]) => { try { this.monitor.start(`init-${name}`); await module.init(); this.monitor.end(`init-${name}`); return { name, success: true }; } catch (e) { this.monitor.error(`模块 ${name} 初始化失败`, e); return { name, success: false, error: e }; } }); const results = await Promise.allSettled(initPromises); const successCount = results.filter(r => r.status === 'fulfilled' && r.value.success).length; this.monitor.log(`模块初始化完成,成功: ${successCount}/${Object.keys(this.modules).length}`); // 设置清理任务 this.setupCleanupTasks(); this.initialized = true; this.monitor.end('total-init'); this.monitor.log('Web Optimizer Enhanced v2.0 初始化完成'); // 显示初始化报告 this.showInitReport(); } catch (e) { this.monitor.error('初始化失败', e); } } setupCleanupTasks() { // 定期清理缓存 const cleanupInterval = setInterval(() => { Object.values(this.modules).forEach(module => { if (module.cssCache) module.cssCache.clear(); if (module.connected) module.connected.clear(); if (module.preloaded) module.preloaded.clear(); }); this.monitor.log('定期清理完成'); }, 10 * 60 * 1000); // 10分钟 this.cleanupTasks.push(() => clearInterval(cleanupInterval)); // 页面卸载时清理 const cleanup = () => { this.destroy(); }; window.addEventListener('beforeunload', cleanup); this.cleanupTasks.push(() => { window.removeEventListener('beforeunload', cleanup); }); } showInitReport() { if (!this.config.get('debug')) return; console.groupCollapsed('[Web Optimizer Enhanced v2.0] 初始化报告'); console.log('版本: 2.0'); console.log('当前域名:', location.hostname); console.log('启用的功能:', Object.entries(this.config.config) .filter(([key, value]) => typeof value === 'object' && value.enabled) .map(([key]) => key) ); // 显示性能计数器 console.log('性能计数:', { 'processed-images': this.monitor.getCounter('processed-images'), 'lazy-loaded-images': this.monitor.getCounter('lazy-loaded-images'), 'preconnected-domains': this.monitor.getCounter('preconnected-domains'), 'preloaded-resources': this.monitor.getCounter('preloaded-resources') }); console.log('配置详情:', this.config.config); console.groupEnd(); } destroy() { if (!this.initialized) return; this.monitor.log('开始清理资源'); // 清理各个模块 Object.values(this.modules).forEach(module => { if (module.destroy) { try { module.destroy(); } catch (e) { this.monitor.warn('模块清理失败', e); } } }); // 执行清理任务 this.cleanupTasks.forEach(task => { try { task(); } catch (e) { this.monitor.warn('清理任务执行失败', e); } }); this.cleanupTasks = []; this.initialized = false; this.monitor.log('资源清理完成'); } // 公共 API - 增强版 updateConfig(path, value) { this.config.set(path, value); this.monitor.log(`配置已更新: ${path} = ${value}`); // 如果是调试模式变更,更新监控器 if (path === 'debug') { this.monitor.debug = value; } } getStats() { return { initialized: this.initialized, version: '2.0', hostname: location.hostname, config: this.config.config, modules: Object.keys(this.modules), counters: { 'processed-images': this.monitor.getCounter('processed-images'), 'lazy-loaded-images': this.monitor.getCounter('lazy-loaded-images'), 'preconnected-domains': this.monitor.getCounter('preconnected-domains'), 'preloaded-resources': this.monitor.getCounter('preloaded-resources') } }; } // 新增:性能报告 getPerformanceReport() { const stats = this.getStats(); const imageStats = { total: document.querySelectorAll('img').length, processed: stats.counters['processed-images'], lazyLoaded: stats.counters['lazy-loaded-images'] }; return { ...stats, performance: { images: imageStats, domains: stats.counters['preconnected-domains'], resources: stats.counters['preloaded-resources'], efficiency: imageStats.total > 0 ? (imageStats.processed / imageStats.total * 100).toFixed(1) + '%' : '0%' } }; } } // ======================== // 全局初始化 // ======================== // 创建全局实例 const optimizer = new WebOptimizer(); // 暴露到全局作用域(调试用) if (typeof window !== 'undefined') { window.WebOptimizer = optimizer; // v2.0 新增:暴露工具函数供调试使用 window.WebOptimizerUtils = { debounce, throttle, safeParseURL, isElementVisible, deepMerge, delay, RetryableOperation }; } // 启动优化器 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => optimizer.init()); } else { // 延迟一点时间,确保页面基本稳定 setTimeout(() => optimizer.init(), 100); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址