Safe performance optimization for Amazon: background throttling, lazy loading, and layout stabilization.
当前为
// ==UserScript==
// @name Amazon Page Smoother
// @namespace http://tampermonkey.net/
// @version 0.0.8
// @description Safe 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 ---
/**
* @typedef {Object} Config
* @property {number} BACKGROUND_TICK_RATE_MS - Minimum interval delay when tab is hidden (ms)
* @property {boolean} DISABLE_ANIMATION_IN_BACKGROUND - Pause animations when tab is hidden
* @property {boolean} ENABLE_CSS_CONTAINMENT - Use CSS containment for rendering optimization
* @property {number} DEBOUNCE_DELAY_MS - Delay for debouncing mutation observer (ms)
* @property {number} CRITICAL_IMAGE_THRESHOLD - Number of top images to prioritize
*/
const CONFIG = {
BACKGROUND_TICK_RATE_MS: 2000,
DISABLE_ANIMATION_IN_BACKGROUND: true,
ENABLE_CSS_CONTAINMENT: true,
DEBOUNCE_DELAY_MS: 200, // Reduced from 200 to align with original fallback
CRITICAL_IMAGE_THRESHOLD: 4 // Heuristic for "above-the-fold" images
};
// --- Feature Detection ---
/**
* @typedef {Object} SupportedFeatures
* @property {boolean} fetchPriority - Supports HTMLImageElement.fetchPriority
* @property {boolean} requestIdleCallback - Supports window.requestIdleCallback
* @property {boolean} closest - Supports Element.closest()
*/
const SUPPORTS = {
fetchPriority: 'fetchPriority' in HTMLImageElement.prototype,
requestIdleCallback: typeof window.requestIdleCallback === 'function',
closest: typeof Element.prototype.closest === 'function'
};
/**
* 1. Critical Safety Check: Exit on sensitive pages (checkout, sign-in, etc.)
* Uses pathname instead of full URL to avoid false positives from query params.
*/
const sensitivePaths = /(checkout|signin|payment|addressselect|huc)/i;
if (sensitivePaths.test(window.location.pathname)) {
console.log('[Amazon Smoother] Sensitive page detected. Script disabled.');
return;
}
/**
* 2. CSS Layout Optimizer: Inject styles for rendering performance
* Uses CSS containment to limit browser reflows/repaints.
*/
function injectOptimizedStyles() {
if (!CONFIG.ENABLE_CSS_CONTAINMENT) return;
// Minified CSS with targeted selectors to avoid over-applying containment
const css = `
/* Optimize search results (specific container) */
.s-main-slot .s-result-item {
content-visibility: auto;
contain-intrinsic-size: 1px 300px; /* Prevents scroll jump */
}
/* Optimize carousels and large sections (avoid over-broad .a-section) */
.a-carousel-viewport, .s-main-slot .a-section {
content-visibility: auto;
}
/* Defer footer rendering until scrolled */
#navFooter, .nav-footer-line {
content-visibility: auto;
contain-intrinsic-size: 1px 500px;
}
/* Reduce animation CPU usage */
.a-spinner-wrapper, .loading-spinner {
will-change: transform, opacity; /* Hint for browser optimizations */
}
/* Hardware-accelerate product images */
img.s-image {
transform: translateZ(0); /* Triggers GPU acceleration */
}
`;
// Fallback if GM_addStyle is unavailable (unlikely with @grant)
if (typeof GM_addStyle === 'function') {
GM_addStyle(css);
} else {
const style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
}
}
/**
* 3. Background Throttler: Slow timers/animations when tab is hidden
* Preserves original behavior when tab is active.
*/
function initializeThrottler() {
// Avoid throttling in iframes to prevent breaking embedded content
if (window.self !== window.top) return;
const originalSetInterval = window.setInterval;
const originalRequestAnimationFrame = window.requestAnimationFrame;
// Throttle short intervals in background
window.setInterval = (callback, delay, ...args) => {
if (document.hidden && delay < CONFIG.BACKGROUND_TICK_RATE_MS) {
return originalSetInterval(callback, CONFIG.BACKGROUND_TICK_RATE_MS, ...args);
}
return originalSetInterval(callback, delay, ...args);
};
// Throttle animations in background (1 FPS)
window.requestAnimationFrame = (callback) => {
if (document.hidden && CONFIG.DISABLE_ANIMATION_IN_BACKGROUND) {
return setTimeout(() => originalRequestAnimationFrame(callback), 1000);
}
return originalRequestAnimationFrame(callback);
};
// Restore original methods when tab becomes visible (reduces side effects)
document.addEventListener('visibilitychange', () => {
if (!document.hidden) {
window.setInterval = originalSetInterval;
window.requestAnimationFrame = originalRequestAnimationFrame;
}
}, { once: false });
}
/**
* 4. Resource Optimizer: Prioritize critical images/iframes
* @param {Node} rootNode - Root node to scan for resources (default: document)
*/
function optimizeResources(rootNode = document) {
if (!rootNode.querySelectorAll) return; // Guard against invalid nodes
// Optimize images
const images = rootNode.querySelectorAll('img:not([data-optimized])');
images.forEach(img => {
// Lazy load non-critical images
if (!img.hasAttribute('loading')) {
img.loading = 'lazy';
}
// Async decoding to avoid main-thread blocking
if (!img.hasAttribute('decoding')) {
img.decoding = 'async';
}
// Prioritize critical images (above-the-fold product images)
if (SUPPORTS.fetchPriority && img.classList.contains('s-image') && SUPPORTS.closest) {
const parentItem = img.closest('.s-result-item');
if (parentItem) {
// Cache result items to avoid re-querying
const resultItems = Array.from(document.querySelectorAll('.s-result-item'));
const isCritical = resultItems.indexOf(parentItem) < CONFIG.CRITICAL_IMAGE_THRESHOLD;
img.fetchPriority = isCritical ? 'high' : 'auto';
}
}
// Deprioritize footer images
if (SUPPORTS.fetchPriority && SUPPORTS.closest) {
if (img.closest('#navFooter') || img.closest('.nav-footer-line')) {
img.fetchPriority = 'low';
}
}
img.dataset.optimized = 'true'; // Mark as processed
});
// Optimize iframes (ads, tracking) with lazy loading
const iframes = rootNode.querySelectorAll('iframe:not([loading])');
iframes.forEach(iframe => {
iframe.loading = 'lazy';
});
}
/**
* 5. Initialization & Dynamic Content Observer
* Debounces optimization calls to avoid excessive re-runs.
*/
function main() {
console.log('[Amazon Smoother] Initializing optimizations...');
try {
injectOptimizedStyles();
initializeThrottler();
optimizeResources();
// Debounce timer for mutation observer
let debounceTimer;
// Observe dynamic content (AJAX-loaded results)
const observer = new MutationObserver(() => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
if (SUPPORTS.requestIdleCallback) {
requestIdleCallback(() => optimizeResources(document.body), { timeout: 1000 });
} else {
optimizeResources(document.body);
}
}, CONFIG.DEBOUNCE_DELAY_MS);
});
// Restrict observer to main content area to reduce overhead
const targetNode = document.querySelector('.s-main-slot') || document.body;
observer.observe(targetNode, {
childList: true,
subtree: true,
attributes: false, // Only track new nodes, not attribute changes
characterData: false
});
} catch (error) {
console.error('[Amazon Smoother] Initialization failed:', error);
}
}
// Initialize safely based on document state
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', main, { once: true });
} else {
main();
}
})();