WebP加载优化(此脚本转为图片加载而设计)

Optimize resource loading by adding lazy loading, WebP support, concurrent requests, and more.

// ==UserScript==
// @name         WebP加载优化(此脚本转为图片加载而设计)
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  Optimize resource loading by adding lazy loading, WebP support, concurrent requests, and more.
// @author       KiwiFruit
// @match        *://*/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // ================== 配置参数 ==================
    const CONFIG = {
        CONCURRENT_BATCH_SIZE: 10, // 每批次并发加载资源数
        PRELOAD_VISIBLE_THRESHOLD: 0.1, // IntersectionObserver 的阈值
        FALLBACK_RETRY_TIMES: 2, // 回退重试次数
        WEBP_FALLBACK_TIMEOUT: 3000, // WebP加载超时时间(ms)
        USE_SKELETON_SCREEN: true, // 是否启用骨架屏(CSS动画)
    };

    // ================== 全局变量 ==================
    let webpSupported = null; // WebP 支持性缓存
    let resourceQueue = []; // 资源队列
    let isProcessingQueue = false;

    // ================== WebP 支持性检测 ==================
    function checkWebPSupport() {
        if (webpSupported !== null) return Promise.resolve(webpSupported);
        return new Promise((resolve) => {
            const webP = new Image();
            webP.src = 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEADsD+JaQAAAB8';
            webP.onload = webP.onerror = () => {
                webpSupported = webP.height === 2;
                resolve(webpSupported);
            };
        });
    }

    // ================== 替换为 WebP 格式 ==================
    function replaceWithWebP(imageElement, src) {
        checkWebPSupport().then(supported => {
            if (supported) {
                // 直接替换 URL,无需额外 fetch
                imageElement.src = src.replace(/\.(jpg|jpeg|png)$/i, '.webp');
            } else {
                imageElement.src = src;
            }
        }).catch(() => {
            imageElement.src = src;
        });
    }

    // ================== 图片加载器 ==================
    function fetchImage(url, retries = CONFIG.FALLBACK_RETRY_TIMES) {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.src = url;

            const timeout = setTimeout(() => {
                img.onerror = img.onload = null;
                if (retries > 0) {
                    console.warn(`Retrying image: ${url}`);
                    fetchImage(url, retries - 1).then(resolve).catch(reject);
                } else {
                    reject(new Error(`Failed to load image: ${url}`));
                }
            }, CONFIG.WEBP_FALLBACK_TIMEOUT);

            img.onload = () => {
                clearTimeout(timeout);
                if (CONFIG.USE_SKELETON_SCREEN) {
                    img.classList.add('loaded');
                }
                resolve(img);
            };

            img.onerror = () => {
                clearTimeout(timeout);
                reject(new Error(`Image load error: ${url}`));
            };
        });
    }

    // ================== 脚本加载器 ==================
    function loadScript(url, retries = CONFIG.FALLBACK_RETRY_TIMES) {
        return new Promise((resolve, reject) => {
            const script = document.createElement('script');
            script.src = url;

            const timeout = setTimeout(() => {
                script.onerror = script.onload = null;
                if (retries > 0) {
                    console.warn(`Retrying script: ${url}`);
                    loadScript(url, retries - 1).then(resolve).catch(reject);
                } else {
                    reject(new Error(`Failed to load script: ${url}`));
                }
            }, CONFIG.WEBP_FALLBACK_TIMEOUT);

            script.onload = () => {
                clearTimeout(timeout);
                resolve(script);
            };

            script.onerror = () => {
                clearTimeout(timeout);
                reject(new Error(`Script load error: ${url}`));
            };

            document.head.appendChild(script);
        });
    }

    // ================== 并发加载资源 ==================
    async function loadResourcesInBatches(resources) {
        if (isProcessingQueue) return;
        isProcessingQueue = true;

        for (let i = 0; i < resources.length; i += CONFIG.CONCURRENT_BATCH_SIZE) {
            const batch = resources.slice(i, i + CONFIG.CONCURRENT_BATCH_SIZE);
            await Promise.allSettled(batch.map(resource => {
                if (resource.type === 'image') {
                    return fetchImage(resource.url).catch(err => {
                        console.error('Image load failed:', err.message);
                        return null;
                    });
                } else if (resource.type === 'script') {
                    return loadScript(resource.url).catch(err => {
                        console.error('Script load failed:', err.message);
                        return null;
                    });
                }
            }));
        }

        isProcessingQueue = false;
    }

    // ================== 懒加载资源 ==================
    function setupLazyLoading(resources) {
        const observer = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    const element = entry.target;
                    observer.unobserve(element);

                    if (element.dataset.type === 'image') {
                        replaceWithWebP(element, element.dataset.src);
                        resourceQueue.push({ type: 'image', url: element.src });
                    } else if (element.dataset.type === 'script') {
                        resourceQueue.push({ type: 'script', url: element.dataset.src });
                    }
                }
            });

            // 触发并发加载
            if (!isProcessingQueue) {
                loadResourcesInBatches(resourceQueue);
                resourceQueue = [];
            }
        }, { threshold: CONFIG.PRELOAD_VISIBLE_THRESHOLD });

        resources.forEach(resource => observer.observe(resource));
    }

    // ================== 预加载首屏资源 ==================
    function preloadFirstScreenResources(resources) {
        const visibleResources = Array.from(resources).filter(resource => {
            const rect = resource.getBoundingClientRect();
            return rect.top >= 0 && rect.bottom <= window.innerHeight;
        });

        visibleResources.forEach(resource => {
            if (resource.dataset.type === 'image') {
                replaceWithWebP(resource, resource.dataset.src);
                resourceQueue.push({ type: 'image', url: resource.src });
            } else if (resource.dataset.type === 'script') {
                resourceQueue.push({ type: 'script', url: resource.dataset.src });
            }
        });

        // 立即加载首屏资源
        if (resourceQueue.length > 0) {
            loadResourcesInBatches(resourceQueue);
            resourceQueue = [];
        }

        // 绑定懒加载
        setupLazyLoading(resources);
    }

    // ================== 初始化入口 ==================
    document.addEventListener('DOMContentLoaded', async () => {
        const resources = document.querySelectorAll('[data-src]');
        if (resources.length === 0) return;

        // 预加载首屏资源
        preloadFirstScreenResources(resources);
    });

    // ================== CSS 动画(骨架屏) ==================
    const style = document.createElement('style');
    style.textContent = `
        img.lazyload {
            opacity: 0.3;
            transition: opacity 0.3s ease-in-out;
        }
        img.lazyload.loaded {
            opacity: 1;
        }
    `;
    document.head.appendChild(style);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址