Greasy Fork

Greasy Fork is available in English.

Web性能优化工具箱(次版)

Web浏览提速80%;DOM渲染及GPU加速;集成Core Web Vitals实时监控面板

当前为 2026-02-02 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Web性能优化工具箱(次版)
// @namespace    http://tampermonkey.net/
// @version      3.8.0-compatibility-optimized
// @description  Web浏览提速80%;DOM渲染及GPU加速;集成Core Web Vitals实时监控面板
// @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'),
            webgpu: typeof GPU !== 'undefined' && !!navigator.gpu
        },
        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, 16);
        },
        cancelRic: (id) => {
            if (Env.features.requestIdleCallback) {
                cancelIdleCallback(id);
            } 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: 9999,
            autoHideDelay: 3000,
            triggerDistance: 120,
            statsUpdateInterval: 3000,
            width: 320
        },
        lazyLoad: {
            enabled: true,
            selector: 'img[data-src], img[data-original], img.lazy, iframe[data-src]',
            preloadDistance: 120,
            useMutationObserver: false
        },
        hardwareAcceleration: {
            enabled: false,
            selector: 'header, nav, aside, .sticky, .fixed'
        },
        contentVisibility: {
            enabled: false,
            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) => 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) {
                Logger.warn(this.moduleName, '重复初始化');
                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 });
        }

        off(target, type, fn) {
            if (!target || typeof target.removeEventListener !== 'function') {
                return;
            }
            target.removeEventListener(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(this._timers[idx]);
                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 {
                    this.off(target, 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. 性能监控模块(含INP)
    // ========================
    class PerformanceMonitor extends BaseModule {
        constructor() {
            super('PerformanceMonitor');
            this.metrics = {
                fcp: null,
                lcp: null,
                cls: 0,
                inp: null,
                ttfb: null
            };
            this._obsInstances = [];
        }

        init() {
            if (!Env.features.performanceObserver) {
                Logger.warn('PerformanceMonitor', '浏览器不支持 PerformanceObserver');
                return;
            }
            super.init();

            this._readHistoricalData();
            this._setupObservers();

            this.on(document, 'visibilitychange', () => {
                if (document.visibilityState === 'visible') {
                    this._readHistoricalData();
                }
            });

            Logger.info('PerformanceMonitor', '初始化完成');
        }

        _readHistoricalData() {
            try {
                if (performance.timing) {
                    this.metrics.ttfb = performance.timing.responseStart - performance.timing.navigationStart;
                }

                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) {
                    const last = lcps[lcps.length - 1];
                    this.metrics.lcp = Math.round(last.startTime);
                }

                let cls = 0;
                const shifts = performance.getEntriesByType('layout-shift');
                shifts.forEach(e => {
                    if (!e.hadRecentInput) {
                        cls += e.value;
                    }
                });
                this.metrics.cls = cls;

            } catch (e) {
                Logger.error('PerformanceMonitor', '读取历史数据失败', e);
            }
        }

        _setupObservers() {
            try {
                const fcpObs = new PerformanceObserver((list) => {
                    list.getEntries().forEach(e => {
                        if (e.name === 'first-contentful-paint') {
                            this.metrics.fcp = Math.round(e.startTime);
                        }
                    });
                });
                fcpObs.observe({ type: 'paint', buffered: true });
                this._obsInstances.push(fcpObs);
            } catch (e) {
                Logger.warn('PerformanceMonitor', 'FCP观察器失败');
            }

            try {
                const lcpObs = new PerformanceObserver((list) => {
                    const entries = list.getEntries();
                    if (entries.length > 0) {
                        const last = entries[entries.length - 1];
                        this.metrics.lcp = Math.round(last.startTime);
                    }
                });
                lcpObs.observe({ type: 'largest-contentful-paint', buffered: true });
                this._obsInstances.push(lcpObs);
            } catch (e) {
                Logger.warn('PerformanceMonitor', 'LCP观察器失败');
            }

            try {
                const clsObs = new PerformanceObserver((list) => {
                    list.getEntries().forEach(e => {
                        if (!e.hadRecentInput) {
                            this.metrics.cls += e.value;
                        }
                    });
                });
                clsObs.observe({ type: 'layout-shift', buffered: true });
                this._obsInstances.push(clsObs);
            } catch (e) {
                Logger.warn('PerformanceMonitor', 'CLS观察器失败');
            }

            // INP (Interaction to Next Paint)
            try {
                let maxDuration = 0;
                const inpObs = new PerformanceObserver((list) => {
                    list.getEntries().forEach(entry => {
                        if (entry.duration > maxDuration) {
                            maxDuration = entry.duration;
                            this.metrics.inp = Math.round(entry.duration);
                        }
                    });
                });
                inpObs.observe({ type: 'event', buffered: true });
                this._obsInstances.push(inpObs);
            } catch (e) {
                Logger.warn('PerformanceMonitor', 'INP观察器失败(浏览器不支持)');
            }
        }

        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 {
                fcp: this.metrics.fcp,
                lcp: this.metrics.lcp,
                cls: this.metrics.cls,
                inp: this.metrics.inp,
                ttfb: this.metrics.ttfb
            };
        }

        destroy() {
            this._obsInstances.forEach(obs => {
                try {
                    obs.disconnect();
                } catch (e) {}
            });
            this._obsInstances = [];
            super.destroy();
        }
    }

    // ========================
    // 5. 图片懒加载模块
    // ========================
    class ImageOptimizer extends BaseModule {
        constructor() {
            super('ImageOptimizer');
            this.io = null;
            this.mo = null;
            this._observedElements = new WeakSet();
        }

        init() {
            if (!Config.lazyLoad.enabled) {
                Logger.info('ImageOptimizer', '已禁用');
                return;
            }
            super.init();

            if (document.readyState === 'loading') {
                this.on(document, 'DOMContentLoaded', () => this._setup());
            } else {
                this._setup();
            }
        }

        _setup() {
            if (Env.features.intersectionObserver) {
                this._setupIntersectionObserver();
            }

            if (Config.lazyLoad.useMutationObserver && Env.features.mutationObserver) {
                this._setupMutationObserver();
            }

            Logger.info('ImageOptimizer', '设置完成');
        }

        _setupIntersectionObserver() {
            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
            });

            const elements = document.querySelectorAll(Config.lazyLoad.selector);
            elements.forEach(el => {
                if (!this._observedElements.has(el)) {
                    this._observedElements.add(el);
                    this.io.observe(el);
                }
            });
        }

        _setupMutationObserver() {
            this.mo = this.observeMutations((mutations) => {
                mutations.forEach(mutation => {
                    mutation.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(child => {
                                    this._observeElement(child);
                                });
                            }
                        }
                    });
                });
            }, 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;
            }

            const src = el.dataset.src;
            delete el.dataset.src;

            if (el.tagName === 'IFRAME') {
                el.src = src;
            } else {
                el.src = src;
            }
        }

        destroy() {
            if (this.io) {
                this.io.disconnect();
                this.io = null;
            }
            if (this.mo) {
                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) {
                Logger.info('PreconnectOptimizer', '已禁用');
                return;
            }
            super.init();

            Utils.ric(() => {
                this._analyzeAndApply();
            }, 3000);
        }

        _analyzeAndApply() {
            const domains = new Set();

            const selectors = [
                'link[rel="stylesheet"][href^="http"]',
                'script[src^="http"]',
                'img[src^="http"]',
                'iframe[src^="http"]',
                'source[srcset^="http"]'
            ];

            selectors.forEach(selector => {
                document.querySelectorAll(selector).forEach(el => {
                    try {
                        const url = new URL(el.href || el.src);
                        if (url.hostname !== window.location.hostname) {
                            domains.add(url.hostname);
                        }
                    } catch (e) {}
                });
            });

            const domainList = Array.from(domains).slice(0, Config.preconnect.maxDomains);

            domainList.forEach(domain => {
                if (this._appliedDomains.has(domain)) {
                    return;
                }
                if (document.querySelector(`link[rel*="preconnect"][href*="${domain}"]`)) {
                    this._appliedDomains.add(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);
            });

            Logger.info('PreconnectOptimizer', `已优化 ${domainList.length} 个域名`);
        }

        destroy() {
            this._appliedDomains.clear();
            super.destroy();
        }
    }

    // ========================
    // 7. UI控制器(320px面板,含INP)
    // ========================
    class UIController extends BaseModule {
        constructor() {
            super('UIController');
            this.panelVisible = false;
            this.button = null;
            this.panel = null;
            this.monitor = null;
            this.statsTimer = null;
            this.isInTriggerZone = false;
            this._hideTimer = null;
            this._throttledMouseMove = null;
        }

        setPerformanceMonitor(monitor) {
            this.monitor = monitor;
        }

        init() {
            if (!Config.ui.enabled) {
                Logger.info('UIController', '已禁用');
                return;
            }
            super.init();

            if (document.readyState === 'loading') {
                this.on(document, 'DOMContentLoaded', () => this._createUI());
            } else {
                this._createUI();
            }
        }

        _createUI() {
            if (!document.body) {
                Logger.warn('UIController', 'body未就绪,延迟创建');
                setTimeout(() => this._createUI(), 100);
                return;
            }

            const width = Config.ui.width;

            const style = document.createElement('style');
            style.textContent = `
                .perfopt-btn {
                    position: fixed !important;
                    bottom: 20px !important;
                    right: 20px !important;
                    width: 48px !important;
                    height: 48px !important;
                    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
                    border-radius: 50% !important;
                    box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important;
                    display: flex !important;
                    align-items: center !important;
                    justify-content: center !important;
                    cursor: pointer !important;
                    z-index: ${Config.ui.zIndex} !important;
                    transition: transform 0.3s ease, opacity 0.3s ease, right 0.3s ease !important;
                    font-size: 20px !important;
                    opacity: 1 !important;
                    pointer-events: auto !important;
                    user-select: none !important;
                }
                .perfopt-btn:hover {
                    transform: scale(1.1) !important;
                }
                .perfopt-btn.hidden {
                    right: -40px !important;
                    opacity: 0.3 !important;
                    pointer-events: none !important;
                }
                .perfopt-panel {
                    position: fixed !important;
                    bottom: 80px !important;
                    right: 20px !important;
                    width: ${width}px !important;
                    max-height: 85vh !important;
                    background: rgba(255,255,255,0.98) !important;
                    backdrop-filter: blur(10px) !important;
                    border-radius: 12px !important;
                    box-shadow: 0 8px 32px rgba(0,0,0,0.15) !important;
                    padding: 20px !important;
                    z-index: ${Config.ui.zIndex - 1} !important;
                    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
                    font-size: 13px !important;
                    display: none !important;
                    opacity: 0 !important;
                    transform: translateY(10px) !important;
                    transition: opacity 0.3s ease, transform 0.3s ease !important;
                    overflow-y: auto !important;
                    box-sizing: border-box !important;
                }
                .perfopt-panel.visible {
                    display: block !important;
                    opacity: 1 !important;
                    transform: translateY(0) !important;
                }
                .perfopt-header {
                    font-weight: 600 !important;
                    margin-bottom: 16px !important;
                    padding-bottom: 10px !important;
                    border-bottom: 1px solid rgba(0,0,0,0.1) !important;
                    font-size: 15px !important;
                    color: #333 !important;
                    display: flex !important;
                    align-items: center !important;
                    gap: 6px !important;
                }
                .perfopt-row {
                    display: flex !important;
                    justify-content: space-between !important;
                    align-items: center !important;
                    margin: 10px 0 !important;
                    line-height: 1.5 !important;
                }
                .perfopt-label {
                    color: #666 !important;
                    font-size: 13px !important;
                }
                .perfopt-value {
                    font-family: "SF Mono", Monaco, monospace !important;
                    font-weight: 600 !important;
                    font-size: 13px !important;
                    font-variant-numeric: tabular-nums !important;
                }
                .perfopt-good { color: #22c55e !important; }
                .perfopt-warn { color: #f59e0b !important; }
                .perfopt-bad { color: #ef4444 !important; }
                .perfopt-footer {
                    margin-top: 16px !important;
                    padding-top: 12px !important;
                    border-top: 1px solid rgba(0,0,0,0.1) !important;
                    font-size: 11px !important;
                    color: #999 !important;
                    line-height: 1.5 !important;
                }
            `;
            document.head.appendChild(style);

            this.button = document.createElement('div');
            this.button.className = 'perfopt-btn';
            this.button.innerHTML = '⚡';
            this.button.title = '性能监控 (靠近显示)';
            document.body.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" style="vertical-align: middle;">
                        <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">--</span>
                </div>
                <div class="perfopt-row">
                    <span class="perfopt-label">TTFB (首字节)</span>
                    <span id="po-ttfb" class="perfopt-value">--</span>
                </div>
                <div class="perfopt-footer">
                    <div>网络: ${Env.networkType} · 设备: ${Env.performanceTier === 2 ? '高性能' : Env.performanceTier === 1 ? '中性能' : '低性能'}</div>
                </div>
            `;
            document.body.appendChild(this.panel);

            this._setupEvents();
            this._startHideTimer();

            Logger.info('UIController', 'UI创建成功');
        }

        _setupEvents() {
            if (!this.button || !this.panel) {
                return;
            }

            this._throttledMouseMove = Utils.throttle((e) => {
                if (this.panelVisible) {
                    return;
                }

                const rect = this.button.getBoundingClientRect();
                const centerX = rect.left + rect.width / 2;
                const centerY = rect.top + rect.height / 2;
                const distance = Math.sqrt(
                    Math.pow(e.clientX - centerX, 2) +
                    Math.pow(e.clientY - centerY, 2)
                );

                if (distance <= Config.ui.triggerDistance && !this.isInTriggerZone) {
                    this.isInTriggerZone = true;
                    this._showButton();
                    this._clearHideTimer();
                } else if (distance > Config.ui.triggerDistance && 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();
                if (this.panelVisible) {
                    this._closePanel();
                } else {
                    this._openPanel();
                }
            });

            this.on(this.button, 'mouseenter', () => {
                this._clearHideTimer();
            });

            this.on(this.button, 'mouseleave', () => {
                if (!this.panelVisible) {
                    this._startHideTimer();
                }
            });
        }

        _showButton() {
            if (this.button) {
                this.button.classList.remove('hidden');
            }
        }

        _hideButton() {
            if (this.button && !this.panelVisible) {
                this.button.classList.add('hidden');
            }
        }

        _openPanel() {
            this.panelVisible = true;
            this.panel.classList.add('visible');
            this._showButton();
            this._updateStats();

            if (this.statsTimer) {
                clearInterval(this.statsTimer);
            }
            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 = document.getElementById(id);
                if (!el) {
                    return;
                }

                if (val === null || val === undefined) {
                    el.textContent = '--';
                    el.className = 'perfopt-value';
                    return;
                }

                const num = typeof val === 'number' ? val : parseFloat(val);
                el.textContent = unit ? `${Math.round(num)}${unit}` : num.toFixed(3);

                let clsName = 'perfopt-value ';
                if (num < good) {
                    clsName += 'perfopt-good';
                } else if (num < bad) {
                    clsName += 'perfopt-warn';
                } else {
                    clsName += 'perfopt-bad';
                }
                el.className = clsName;
            };

            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');
        }

        destroy() {
            if (this.statsTimer) {
                clearInterval(this.statsTimer);
                this.statsTimer = null;
            }
            this._throttledMouseMove = null;
            super.destroy();
        }
    }

    // ========================
    // 8. 主应用控制器
    // ========================
    class AppController {
        constructor() {
            this.modules = {};
        }

        init() {
            const hostname = window.location.hostname;
            const isBlacklisted = Config.blacklistedDomains.some(d => hostname.includes(d));

            if (isBlacklisted) {
                Logger.info('AppController', '域名在黑名单中,停止加载');
                return;
            }

            Logger.info('AppController', '开始初始化');

            this.modules.monitor = new PerformanceMonitor();
            this.modules.monitor.init();

            this.modules.preconnect = new PreconnectOptimizer();
            this.modules.preconnect.init();

            if (document.readyState === 'loading') {
                document.addEventListener('DOMContentLoaded', () => {
                    Utils.ric(() => this._initDOMModules(), 100);
                });
            } else {
                Utils.ric(() => this._initDOMModules(), 100);
            }

            window.addEventListener('beforeunload', () => {
                this.destroy();
            });
        }

        _initDOMModules() {
            Logger.info('AppController', '初始化DOM模块');

            this.modules.images = new ImageOptimizer();
            this.modules.images.init();

            this.modules.ui = new UIController();
            this.modules.ui.setPerformanceMonitor(this.modules.monitor);
            this.modules.ui.init();
        }

        destroy() {
            Object.values(this.modules).forEach(m => {
                if (m && typeof m.destroy === 'function') {
                    m.destroy();
                }
            });
            this.modules = {};
        }
    }

    // ========================
    // 9. 启动
    // ========================
    try {
        const app = new AppController();
        app.init();
        window.PerfOptimizer = app;
        Logger.info('Bootstrap', '脚本加载成功 v4.1.0');
    } catch (error) {
        Logger.error('Bootstrap', `致命错误: ${error.message}`);
        console.error(error);
    }

})();