// ==UserScript== // @name Web性能优化工具箱(主版-最终优化版) // @namespace http://tampermonkey.net/ // @version 3.8.3-final // @description SPA兼容懒加载+预连接+硬件加速+集成Core Web Vitals面板(空闲调度完美适配/Shadow DOM隔离/安全补丁) // @author KiwiFruit // @match *://*/* // @exclude *://*.weibo.com/* // @exclude *://*.x.com/* // @exclude *://*.chat.z.ai* // @exclude *://*.doubao.com/* // @grant none // @license MIT // @run-at document-start // @downloadURL https://update.greasyfork.icu/scripts/529387/Web%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E5%B7%A5%E5%85%B7%E7%AE%B1%EF%BC%88%E4%B8%BB%E7%89%88-%E6%9C%80%E7%BB%88%E4%BC%98%E5%8C%96%E7%89%88%EF%BC%89.user.js // @updateURL https://update.greasyfork.icu/scripts/529387/Web%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E5%B7%A5%E5%85%B7%E7%AE%B1%EF%BC%88%E4%B8%BB%E7%89%88-%E6%9C%80%E7%BB%88%E4%BC%98%E5%8C%96%E7%89%88%EF%BC%89.meta.js // ==/UserScript== (function() { 'use strict'; // ======================== // 1. 环境检测与工具函数(增强版) // ======================== const Env = { features: { nativeLazyLoad: 'loading' in HTMLImageElement.prototype, intersectionObserver: 'IntersectionObserver' in window, mutationObserver: 'MutationObserver' in window, performanceObserver: 'PerformanceObserver' in window, requestIdleCallback: 'requestIdleCallback' in window, contentVisibility: CSS.supports('content-visibility', 'hidden'), shadowDOM: 'attachShadow' in Element.prototype }, performanceTier: (() => { if (navigator.hardwareConcurrency >= 4) return 2; if (window.devicePixelRatio <= 1.5) return 1; return 0; })(), networkType: navigator.connection?.effectiveType || 'unknown' }; const Utils = { ric: (cb, timeout = 2000) => { if (Env.features.requestIdleCallback) { return requestIdleCallback(cb, { timeout }); } return setTimeout(() => cb({ timeRemaining: () => 16, didTimeout: false }), 16); }, cancelRic: (id) => { if (!id) return; if (Env.features.requestIdleCallback) { try { cancelIdleCallback(id); } catch (e) {} } else { clearTimeout(id); } }, throttle: (fn, limit) => { let inThrottle; return function(...args) { if (!inThrottle) { fn.apply(this, args); inThrottle = true; setTimeout(() => { inThrottle = false; }, limit); } }; } }; // ======================== // 2. 统一配置 // ======================== const Config = { debug: false, ui: { enabled: true, zIndex: 2147483647, autoHideDelay: 3000, triggerDistance: 120, statsUpdateInterval: 3000, width: 320 }, lazyLoad: { enabled: true, selector: 'img[data-src]:not([data-src=""]), img[original-src], img[data-original], img.lazy, iframe[data-src]', preloadDistance: 120, useMutationObserver: true }, hardwareAcceleration: { enabled: true, selector: 'header, nav, aside, .sticky, .fixed' }, contentVisibility: { enabled: true, selector: '[data-perfopt-cv]' }, preconnect: { enabled: true, dynamic: true, maxDomains: 6 }, blacklistedDomains: ['weibo.com', 'weibo.cn', 'x.com', 'chat.z.ai', 'doubao.com', 'kimi.com', 'qianwen.com'] }; const Logger = { info: (m, msg) => Config.debug && console.log(`[PerfOpt][${m}]`, msg), warn: (m, msg) => console.warn(`[PerfOpt][${m}]`, msg), error: (m, msg) => console.error(`[PerfOpt][${m}]`, msg) }; // ======================== // 3. 基础模块类 // ======================== class BaseModule { constructor(name) { this.moduleName = name; this.initialized = false; this._handlers = []; this._timers = []; this._observers = []; } init() { if (this.initialized) return; this.initialized = true; } on(target, type, fn, options) { if (!target || typeof target.addEventListener !== 'function') return; target.addEventListener(type, fn, options); this._handlers.push({ target, type, fn }); } setTimer(fn, delay) { const id = setTimeout(fn, delay); this._timers.push(id); return id; } clearTimer(id) { const idx = this._timers.indexOf(id); if (idx > -1) { clearTimeout(id); this._timers.splice(idx, 1); } } setInterval(fn, delay) { const id = setInterval(fn, delay); this._timers.push(id); return id; } observeMutations(callback, target, options) { if (!Env.features.mutationObserver || !target) return null; const mo = new MutationObserver(Utils.throttle(callback, 100)); mo.observe(target, options || { childList: true, subtree: true }); this._observers.push(mo); return mo; } destroy() { this._handlers.forEach(({ target, type, fn }) => { try { target.removeEventListener(type, fn); } catch (e) {} }); this._handlers = []; this._timers.forEach(id => { try { clearTimeout(id); clearInterval(id); } catch (e) {} }); this._timers = []; this._observers.forEach(obs => { try { obs.disconnect(); } catch (e) {} }); this._observers = []; this.initialized = false; } } // ======================== // 4. 统一性能监控模块 // ======================== class UnifiedPerformanceMonitor extends BaseModule { constructor() { super('UnifiedPerformanceMonitor'); this.metrics = { fcp: null, lcp: null, cls: 0, inp: null, ttfb: null, longTasks: [] }; this._obsInstances = []; } init() { if (!Env.features.performanceObserver) { Logger.warn(this.moduleName, '浏览器不支持 PerformanceObserver'); return; } super.init(); this._readHistoricalData(); this._setupObservers(); this.on(document, 'visibilitychange', () => { if (document.visibilityState === 'visible') this._readHistoricalData(); }); } _readHistoricalData() { try { const navEntry = performance.getEntriesByType('navigation')[0]; if (navEntry) this.metrics.ttfb = Math.round(navEntry.responseStart); const paints = performance.getEntriesByType('paint'); paints.forEach(e => { if (e.name === 'first-contentful-paint' && !this.metrics.fcp) this.metrics.fcp = Math.round(e.startTime); }); const lcps = performance.getEntriesByType('largest-contentful-paint'); if (lcps.length > 0) this.metrics.lcp = Math.round(lcps[lcps.length - 1].startTime); let cls = 0; performance.getEntriesByType('layout-shift').forEach(e => { if (!e.hadRecentInput) cls += e.value; }); this.metrics.cls = cls; this.metrics.longTasks = performance.getEntriesByType('longtask').map(t => ({ duration: t.duration, startTime: t.startTime })); } catch (e) { Logger.error(this.moduleName, '读取历史数据失败', e); } } _setupObservers() { const addObs = (type, cb) => { try { const obs = new PerformanceObserver(cb); // 修复:INP 明确支持 buffered,恢复以捕获早期交互;其他类型按需关闭避免警告 obs.observe({ type, buffered: type === 'event' }); this._obsInstances.push(obs); } catch (e) { Logger.warn(this.moduleName, `${type} 观察器失败`); } }; addObs('paint', list => { list.getEntries().forEach(e => { if (e.name === 'first-contentful-paint' && !this.metrics.fcp) this.metrics.fcp = Math.round(e.startTime); }); }); addObs('largest-contentful-paint', list => { const entries = list.getEntries(); if (entries.length > 0) this.metrics.lcp = Math.round(entries[entries.length - 1].startTime); }); addObs('layout-shift', list => { list.getEntries().forEach(e => { if (!e.hadRecentInput) this.metrics.cls += e.value; }); }); addObs('event', list => { let maxDuration = 0; list.getEntries().forEach(entry => { if (entry.duration > maxDuration) { maxDuration = entry.duration; this.metrics.inp = Math.round(entry.duration); } }); }); addObs('longtask', list => { list.getEntries().forEach(entry => { this.metrics.longTasks.push({ duration: entry.duration, startTime: entry.startTime }); }); }); } getMetrics() { try { const lcps = performance.getEntriesByType('largest-contentful-paint'); if (lcps.length > 0) { const last = lcps[lcps.length - 1]; if (last.startTime > (this.metrics.lcp || 0)) this.metrics.lcp = Math.round(last.startTime); } } catch (e) {} return { ...this.metrics, longTasks: this.metrics.longTasks.filter(t => t.duration > 200) }; } destroy() { this._obsInstances.forEach(obs => { try { obs.disconnect(); } catch (e) {} }); this._obsInstances = []; super.destroy(); } } // ======================== // 5. 统一图片懒加载模块 // ======================== class UnifiedImageOptimizer extends BaseModule { constructor() { super('UnifiedImageOptimizer'); this.io = null; this.mo = null; this._observedElements = new WeakSet(); } init() { if (!Config.lazyLoad.enabled) return; super.init(); if (document.readyState === 'loading') this.on(document, 'DOMContentLoaded', () => this._setup()); else this._setup(); } _setup() { if (Env.features.intersectionObserver) { this.io = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { this._loadImage(entry.target); this.io.unobserve(entry.target); this._observedElements.delete(entry.target); } }); }, { rootMargin: `${Config.lazyLoad.preloadDistance}px 0px`, threshold: 0.01 }); document.querySelectorAll(Config.lazyLoad.selector).forEach(el => { if (!this._observedElements.has(el)) { this._observedElements.add(el); this.io.observe(el); } }); } if (Config.lazyLoad.useMutationObserver && Env.features.mutationObserver) { this.mo = this.observeMutations((mutations) => { mutations.forEach(m => m.addedNodes.forEach(node => { if (node.nodeType === 1) { if (node.matches && node.matches(Config.lazyLoad.selector)) this._observeElement(node); if (node.querySelectorAll) node.querySelectorAll(Config.lazyLoad.selector).forEach(this._observeElement.bind(this)); } })); }, document.body, { childList: true, subtree: true }); } } _observeElement(el) { if (!this.io || this._observedElements.has(el)) return; this._observedElements.add(el); this.io.observe(el); } _loadImage(el) { if (!el.dataset.src) return; el.src = el.dataset.src; delete el.dataset.src; if (el.tagName !== 'IFRAME') el.decoding = 'async'; } destroy() { this.io?.disconnect(); this.io = null; this.mo?.disconnect(); this.mo = null; super.destroy(); } } // ======================== // 6. 预连接优化模块 // ======================== class PreconnectOptimizer extends BaseModule { constructor() { super('PreconnectOptimizer'); this._appliedDomains = new Set(); } init() { if (!Config.preconnect.enabled) return; super.init(); // 交给空闲调度器执行,绝对不阻塞首屏 Utils.ric(() => this._analyzeAndApply(), 3000); } _analyzeAndApply() { const domains = new Set(); ['link[rel="stylesheet"][href^="http"]', 'script[src^="http"]', 'img[src^="http"]', 'iframe[src^="http"]'].forEach(sel => { document.querySelectorAll(sel).forEach(el => { try { const url = new URL(el.href || el.src); if (url.hostname !== window.location.hostname) domains.add(url.hostname); } catch (e) {} }); }); Array.from(domains).slice(0, Config.preconnect.maxDomains).forEach(domain => { if (this._appliedDomains.has(domain) || document.querySelector(`link[rel*="preconnect"][href*="${domain}"]`)) return; const link = document.createElement('link'); link.rel = 'preconnect'; link.href = `https://${domain}`; link.crossOrigin = 'anonymous'; document.head.appendChild(link); this._appliedDomains.add(domain); }); } destroy() { this._appliedDomains.clear(); super.destroy(); } } // ======================== // 7. 统一滚动优化模块 (安全版) // ======================== class UnifiedScrollOptimizer extends BaseModule { constructor() { super('UnifiedScrollOptimizer'); this._originalAddEventListener = null; } init() { super.init(); this._optimizePassiveEvents(); this._applyHardwareAcceleration(); } _optimizePassiveEvents() { this._originalAddEventListener = EventTarget.prototype.addEventListener; const self = this; const passiveEvents = ['wheel', 'mousewheel', 'touchstart', 'touchmove', 'scroll']; EventTarget.prototype.addEventListener = function(type, handler, options) { let safeOptions = options; if (passiveEvents.includes(type)) { const isRoot = this === window || this === document || this === document.body; if (isRoot) { if (typeof safeOptions === 'boolean') { safeOptions = { capture: safeOptions, passive: true }; } else if (typeof safeOptions === 'object') { // 🔑 安全补丁:仅当未显式声明 passive: false 时才强制开启,兼容 preventDefault if (safeOptions.passive !== false) safeOptions = { ...safeOptions, passive: true }; } else { safeOptions = { passive: true }; } } } return self._originalAddEventListener.call(this, type, handler, safeOptions); }; } _applyHardwareAcceleration() { if (!Config.hardwareAcceleration.enabled) return; // 🔑 移除错误的 WebGPU 判断 document.querySelectorAll(Config.hardwareAcceleration.selector).forEach(el => { if (!el.style.willChange) { el.style.willChange = 'transform'; el.style.transform = 'translateZ(0)'; } }); } destroy() { if (this._originalAddEventListener) { EventTarget.prototype.addEventListener = this._originalAddEventListener; this._originalAddEventListener = null; } super.destroy(); } } // ======================== // 8. 内容可见性优化模块 // ======================== class ContentVisibilityOptimizer extends BaseModule { init() { if (!Config.contentVisibility.enabled || !Env.features.contentVisibility) return; super.init(); this._apply(); if (Config.lazyLoad.useMutationObserver) { this.observeMutations(() => this._apply(), document.body, { childList: true, subtree: true }); } } _apply() { document.querySelectorAll(Config.contentVisibility.selector).forEach(el => { if (!el.style.contentVisibility) { el.style.contentVisibility = 'auto'; el.style.containIntrinsicSize = '0 500px'; } }); } destroy() { super.destroy(); } } // ======================== // 9. 统一空闲任务调度(完美适配版) // ======================== class UnifiedIdleScheduler extends BaseModule { constructor() { super('UnifiedIdleScheduler'); this.tasks = []; this._isRunning = false; this._pendingId = null; } init() { super.init(); // 页面完全加载后自动触发一次调度,确保非关键任务在资源空闲后执行 const trigger = () => this.schedule(); if (document.readyState === 'complete') Utils.ric(trigger, 1000); else window.addEventListener('load', () => Utils.ric(trigger, 1000), { once: true }); } /** * 添加空闲任务 * @param {Function} fn 任务函数 * @param {string} priority 优先级 high | normal | low * @param {string} [id] 可选任务标识,用于后续取消 */ add(fn, priority = 'normal', id = null) { if (typeof fn !== 'function') return; // 过滤重复ID if (id && this.tasks.some(t => t.id === id)) return; this.tasks.push({ fn, priority, id }); this._sortTasks(); if (!this._isRunning) this.schedule(); } cancel(id) { this.tasks = this.tasks.filter(t => t.id !== id); } _sortTasks() { const p = { high: 0, normal: 1, low: 2 }; this.tasks.sort((a, b) => (p[a.priority] || 1) - (p[b.priority] || 1)); } schedule() { if (this.tasks.length === 0) { this._isRunning = false; this._pendingId = null; return; } this._isRunning = true; // 🔑 核心优化:严格非阻塞检查 + 超时兜底 this._pendingId = Utils.ric((deadline) => { // 条件1:剩余时间充足(>50ms) 或 浏览器已强制超时(didTimeout=true) 则执行 // 条件2:否则主动让出主线程,等待下一次空闲 const canRun = deadline.timeRemaining() > 50 || deadline.didTimeout; if (canRun && this.tasks.length > 0) { while (this.tasks.length > 0 && (deadline.timeRemaining() > 50 || deadline.didTimeout)) { const task = this.tasks.shift(); try { task.fn(); } catch (e) { Logger.error(this.moduleName, e); } } } // 队列仍有任务,继续调度;否则标记空闲 if (this.tasks.length > 0) this.schedule(); else { this._isRunning = false; this._pendingId = null; } }, { timeout: 2000 }); // 2秒超时保证任务最终被执行 } destroy() { if (this._pendingId) { Utils.cancelRic(this._pendingId); this._pendingId = null; } this.tasks = []; this._isRunning = false; super.destroy(); } } // ======================== // 10. UI控制器 (Shadow DOM 隔离版) // ======================== class UIController extends BaseModule { constructor() { super('UIController'); this.panelVisible = false; this.button = this.panel = this.host = this.shadow = null; this.monitor = null; this.statsTimer = this._hideTimer = this._throttledMouseMove = null; this.isInTriggerZone = false; } setPerformanceMonitor(monitor) { this.monitor = monitor; } init() { if (!Config.ui.enabled) return; super.init(); if (document.readyState === 'loading') this.on(document, 'DOMContentLoaded', () => this._createUI()); else this._createUI(); } _createUI() { if (!document.body) return setTimeout(() => this._createUI(), 100); if (!Env.features.shadowDOM) { Logger.warn('UIController', '不支持 Shadow DOM,UI 降级隐藏'); return; } this.host = document.createElement('div'); this.host.style.cssText = `position:fixed;inset:0;pointer-events:none;z-index:${Config.ui.zIndex};`; document.body.appendChild(this.host); this.shadow = this.host.attachShadow({ mode: 'open' }); const style = document.createElement('style'); style.textContent = ` :host { all: initial; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; } .perfopt-btn { position: absolute; bottom: 20px; right: 20px; width: 48px; height: 48px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 50%; box-shadow: 0 4px 12px rgba(0,0,0,0.15); display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 20px; transition: all 0.3s ease; pointer-events: auto; user-select: none; } .perfopt-btn:hover { transform: scale(1.1); } .perfopt-btn.hidden { right: -40px; opacity: 0.3; pointer-events: none; } .perfopt-panel { position: absolute; bottom: 80px; right: 20px; width: ${Config.ui.width}px; max-height: 85vh; background: rgba(255,255,255,0.98); backdrop-filter: blur(10px); border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.15); padding: 20px; font-size: 13px; display: none; opacity: 0; transform: translateY(10px); transition: all 0.3s ease; overflow-y: auto; box-sizing: border-box; pointer-events: auto; } .perfopt-panel.visible { display: block; opacity: 1; transform: translateY(0); } .perfopt-header { font-weight: 600; margin-bottom: 16px; padding-bottom: 10px; border-bottom: 1px solid rgba(0,0,0,0.1); font-size: 15px; color: #333; display: flex; align-items: center; gap: 6px; } .perfopt-row { display: flex; justify-content: space-between; align-items: center; margin: 10px 0; line-height: 1.5; } .perfopt-label { color: #666; } .perfopt-value { font-family: "SF Mono", Monaco, monospace; font-weight: 600; font-variant-numeric: tabular-nums; } .perfopt-good { color: #22c55e; } .perfopt-warn { color: #f59e0b; } .perfopt-bad { color: #ef4444; } .perfopt-footer { margin-top: 16px; padding-top: 12px; border-top: 1px solid rgba(0,0,0,0.1); font-size: 11px; color: #999; } `; this.shadow.appendChild(style); this.button = document.createElement('div'); this.button.className = 'perfopt-btn hidden'; this.button.innerHTML = '⚡'; this.button.title = '性能监控 (靠近显示)'; this.shadow.appendChild(this.button); this.panel = document.createElement('div'); this.panel.className = 'perfopt-panel'; this.panel.innerHTML = `