Greasy Fork is available in English.
SPA兼容懒加载+预连接+硬件加速+集成Core Web Vitals面板(空闲调度完美适配/Shadow DOM隔离/安全补丁)
// ==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
// ==/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 = `
<div class="perfopt-header">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>
Core Web Vitals
</div>
<div class="perfopt-row"><span class="perfopt-label">FCP (首次绘制)</span><span id="po-fcp" class="perfopt-value">--</span></div>
<div class="perfopt-row"><span class="perfopt-label">LCP (最大内容)</span><span id="po-lcp" class="perfopt-value">--</span></div>
<div class="perfopt-row"><span class="perfopt-label">CLS (布局偏移)</span><span id="po-cls" class="perfopt-value">--</span></div>
<div class="perfopt-row"><span class="perfopt-label">INP (交互延迟)</span><span id="po-inp" class="perfopt-value" title="需用户首次交互后计算">等待交互</span></div>
<div class="perfopt-row"><span class="perfopt-label">TTFB (首字节)</span><span id="po-ttfb" class="perfopt-value">--</span></div>
<div class="perfopt-row"><span class="perfopt-label">Long Tasks (卡顿)</span><span id="po-longtasks" class="perfopt-value">--</span></div>
<div class="perfopt-footer">网络: ${Env.networkType} · 设备: ${Env.performanceTier === 2 ? '高性能' : Env.performanceTier === 1 ? '中性能' : '低性能'}</div>
`;
this.shadow.appendChild(this.panel);
this._setupEvents();
this._startHideTimer();
}
_setupEvents() {
this._throttledMouseMove = Utils.throttle((e) => {
if (this.panelVisible) return;
const rect = this.button.getBoundingClientRect();
const dist = Math.hypot(e.clientX - (rect.left + rect.width/2), e.clientY - (rect.top + rect.height/2));
if (dist <= Config.ui.triggerDistance) {
if (!this.isInTriggerZone) { this.isInTriggerZone = true; this._showButton(); this._clearHideTimer(); }
} else if (this.isInTriggerZone) {
this.isInTriggerZone = false; this._startHideTimer();
}
}, 50);
this.on(document, 'mousemove', this._throttledMouseMove);
this.on(document, 'click', (e) => {
if (this.panelVisible && !this.button.contains(e.target) && !this.panel.contains(e.target)) this._closePanel();
});
this.on(this.button, 'click', (e) => {
e.stopPropagation();
this.panelVisible ? this._closePanel() : this._openPanel();
});
this.on(this.button, 'mouseenter', () => this._clearHideTimer());
this.on(this.button, 'mouseleave', () => !this.panelVisible && this._startHideTimer());
}
_showButton() { this.button.classList.remove('hidden'); }
_hideButton() { if (!this.panelVisible) this.button.classList.add('hidden'); }
_openPanel() {
this.panelVisible = true; this.panel.classList.add('visible'); this._showButton(); this._updateStats();
this.statsTimer = this.setInterval(() => this._updateStats(), Config.ui.statsUpdateInterval);
}
_closePanel() {
this.panelVisible = false; this.panel.classList.remove('visible'); this._startHideTimer();
if (this.statsTimer) { clearInterval(this.statsTimer); this.statsTimer = null; }
}
_startHideTimer() { this._clearHideTimer(); this._hideTimer = this.setTimer(() => this._hideButton(), Config.ui.autoHideDelay); }
_clearHideTimer() { if (this._hideTimer) { this.clearTimer(this._hideTimer); this._hideTimer = null; } }
_updateStats() {
if (!this.monitor) return;
const m = this.monitor.getMetrics();
const setVal = (id, val, good, bad, unit = '') => {
const el = this.shadow.getElementById(id);
if (!el) return;
if (val === null || val === undefined) {
el.textContent = id === 'po-inp' ? '等待交互' : '--';
el.className = 'perfopt-value';
return;
}
const num = typeof val === 'number' ? val : parseFloat(val);
el.textContent = unit ? `${Math.round(num)}${unit}` : num.toFixed(3);
let cls = 'perfopt-value ';
cls += num < good ? 'perfopt-good' : num < bad ? 'perfopt-warn' : 'perfopt-bad';
el.className = cls;
};
setVal('po-fcp', m.fcp, 1800, 3000, 'ms');
setVal('po-lcp', m.lcp, 2500, 4000, 'ms');
setVal('po-cls', m.cls, 0.1, 0.25);
setVal('po-inp', m.inp, 200, 500, 'ms');
setVal('po-ttfb', m.ttfb, 600, 1000, 'ms');
setVal('po-longtasks', m.longTasks.length, 0, 3, '次');
}
destroy() {
if (this.statsTimer) { clearInterval(this.statsTimer); this.statsTimer = null; }
this.host?.remove(); this.host = this.shadow = this.button = this.panel = null;
super.destroy();
}
}
// ========================
// 11. 主应用控制器
// ========================
class UnifiedAppController {
constructor() { this.modules = {}; }
init() {
const hostname = window.location.hostname;
if (Config.blacklistedDomains.some(d => hostname.includes(d))) return;
this.modules.monitor = new UnifiedPerformanceMonitor();
this.modules.monitor.init();
this.modules.preconnect = new PreconnectOptimizer();
this.modules.preconnect.init();
this.modules.idleScheduler = new UnifiedIdleScheduler();
this.modules.idleScheduler.init();
this.modules.scrollOptimizer = new UnifiedScrollOptimizer();
this.modules.scrollOptimizer.init();
this.modules.cvOptimizer = new ContentVisibilityOptimizer();
this.modules.cvOptimizer.init();
const initDOM = () => {
this.modules.images = new UnifiedImageOptimizer();
this.modules.images.init();
this.modules.ui = new UIController();
this.modules.ui.setPerformanceMonitor(this.modules.monitor);
this.modules.ui.init();
};
// 🔑 修复:使用原生事件绑定,避免 this.on 报错
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initDOM);
} else {
initDOM();
}
const cleanup = () => this.destroy();
window.addEventListener('beforeunload', cleanup);
window.addEventListener('pagehide', cleanup);
}
destroy() {
Object.values(this.modules).forEach(m => m?.destroy?.());
this.modules = {};
}
}
// ========================
// 12. 启动脚本
// ========================
try {
const app = new UnifiedAppController();
app.init();
window.UnifiedPerfOptimizer = app;
if (Config.debug) console.log('[PerfOpt] 脚本加载成功 v4.0.0-final');
} catch (error) {
console.error('[PerfOpt] 致命错误:', error);
}
})();