Amazon Page Smoother

Performance optimization for Amazon: background throttling, lazy loading, and layout stabilization.

目前為 2025-11-26 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Amazon Page Smoother
// @namespace    http://tampermonkey.net/
// @version      0.1.0
// @description  Performance optimization for Amazon: background throttling, lazy loading, and layout stabilization.
// @license      Unlicense
// @author       VeleSila
// @match        https://www.amazon.com/*
// @match        https://www.amazon.co.jp/*
// @match        https://www.amazon.co.uk/*
// @match        https://www.amazon.es/*
// @match        https://www.amazon.fr/*
// @match        https://www.amazon.de/*
// @match        https://www.amazon.it/*
// @match        https://www.amazon.ca/*
// @match        https://www.amazon.com.au/*
// @exclude      */cart/*
// @exclude      */buy/*
// @exclude      */checkout/*
// @exclude      */gp/buy/*
// @grant        GM_addStyle
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    /**
     * CONFIGURATION
     */
    const CONFIG = {
        // Number of images to load with high priority (Above the fold approximation)
        HIGH_PRIORITY_COUNT: 4,
        // Debounce delay for the mutation observer (ms)
        DEBOUNCE_DELAY_MS: 300
    };

    /**
     * 1. CRITICAL SAFETY CHECK
     * Immediately exit on sensitive pages to prevent interference with payments.
     */
    const sensitivePaths = /(checkout|signin|payment|addressselect|huc)/i;
    if (sensitivePaths.test(window.location.pathname)) {
        return;
    }

    /**
     * 2. CSS OPTIMIZER
     * Uses modern CSS containment to prevent rendering off-screen content.
     * Adjusted to minimize layout shifting.
     */
    function injectOptimizedStyles() {
        const css = `
            /* Optimize Search Results 
               Using strict containment on result items allows the browser to 
               skip rendering items not currently in the viewport.
            */
            .s-main-slot .s-result-item {
                content-visibility: auto;
                contain-intrinsic-size: 1px 350px; /* Adjusted to average card height */
            }

            /* GPU Acceleration for Product Images */
            img.s-image {
                transform: translateZ(0);
                will-change: opacity;
            }

            /* Defer Footer Rendering */
            #navFooter {
                content-visibility: auto;
                contain-intrinsic-size: 1px 600px;
            }
        `;

        if (typeof GM_addStyle === 'function') {
            GM_addStyle(css);
        } else {
            const style = document.createElement('style');
            style.textContent = css;
            document.head.appendChild(style);
        }
    }

    /**
     * 3. RESOURCE OPTIMIZER
     * Applies lazy loading and fetch priority attributes.
     * REVISION: Removed getBoundingClientRect() calls to prevent Layout Thrashing.
     */
    function optimizeResources(rootNode = document) {
        // Select images that haven't been optimized yet
        const images = rootNode.querySelectorAll('img:not([data-opt])');
        if (images.length === 0) return;

        // Check for browser support
        const supportsPriority = 'fetchPriority' in HTMLImageElement.prototype;

        for (let i = 0, len = images.length; i < len; i++) {
            const img = images[i];
            
            // Mark as processed to avoid re-looping
            img.dataset.opt = '1';

            // 1. Footer Images: Always low priority and lazy
            if (img.closest('#navFooter')) {
                img.loading = 'lazy';
                img.decoding = 'async';
                if (supportsPriority) img.fetchPriority = 'low';
                continue;
            }

            // 2. Product Images (Search results)
            if (img.classList.contains('s-image')) {
                // Heuristic: The first few images found in the DOM are likely "Above the fold"
                // We use the loop index rather than geometry to avoid Reflow.
                if (i < CONFIG.HIGH_PRIORITY_COUNT) {
                    img.loading = 'eager';
                    if (supportsPriority) img.fetchPriority = 'high';
                } else {
                    img.loading = 'lazy';
                    img.decoding = 'async';
                    if (supportsPriority) img.fetchPriority = 'low';
                }
                continue;
            }

            // 3. General Fallback for other images
            if (!img.hasAttribute('loading')) {
                img.loading = 'lazy';
                img.decoding = 'async';
            }
        }
    }

    /**
     * 4. INITIALIZATION & OBSERVER
     * Handles dynamic content loading (Infinite scroll/Pagination).
     */
    function main() {
        try {
            injectOptimizedStyles();
            
            // Initial pass
            optimizeResources(document);

            // Debounced Observer for dynamic content
            let debounceTimer;
            const observer = new MutationObserver((mutations) => {
                // Check if nodes were actually added
                let hasNewNodes = false;
                for (const m of mutations) {
                    if (m.addedNodes.length > 0) {
                        hasNewNodes = true;
                        break;
                    }
                }
                if (!hasNewNodes) return;

                clearTimeout(debounceTimer);
                debounceTimer = setTimeout(() => {
                    // Use requestIdleCallback if available for non-urgent updates
                    if ('requestIdleCallback' in window) {
                        requestIdleCallback(() => optimizeResources(document.body));
                    } else {
                        optimizeResources(document.body);
                    }
                }, CONFIG.DEBOUNCE_DELAY_MS);
            });

            // Target the search slot specifically if it exists, otherwise fallback to body
            const targetNode = document.querySelector('.s-main-slot') || document.body;
            
            observer.observe(targetNode, {
                childList: true,
                subtree: true
            });

            console.log('[Amazon Smoother] Optimized v1.0.0 active.');

        } catch (error) {
            console.error('[Amazon Smoother] Error:', error);
        }
    }

    // Boot Logic
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', main, { once: true });
    } else {
        main();
    }

})();