Performance optimization for Amazon: background throttling, lazy loading, and layout stabilization.
当前为
// ==UserScript==
// @name Amazon Page Smoother
// @namespace http://tampermonkey.net/
// @version 0.0.9
// @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 = {
// Minimum interval delay when tab is hidden (ms)
BACKGROUND_TICK_RATE_MS: 2000,
// Pause animations when tab is hidden
DISABLE_ANIMATION_IN_BACKGROUND: true,
// Use CSS containment for rendering optimization
ENABLE_CSS_CONTAINMENT: true,
// Delay for debouncing mutation observer (ms)
DEBOUNCE_DELAY_MS: 300,
// Pixel height to consider "above the fold" for priority loading
CRITICAL_VIEWPORT_HEIGHT: 1200
};
const SUPPORTS = {
fetchPriority: 'fetchPriority' in HTMLImageElement.prototype,
requestIdleCallback: typeof window.requestIdleCallback === 'function',
closest: typeof Element.prototype.closest === 'function'
};
/**
* 1. Critical Safety Check
* Exit immediately on sensitive pages (checkout, payments, etc).
*/
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
* Uses CSS Containment to prevent rendering off-screen content.
*/
function injectOptimizedStyles() {
if (!CONFIG.ENABLE_CSS_CONTAINMENT) return;
const css = `
/* Optimize Search Results */
.s-main-slot .s-result-item {
content-visibility: auto;
contain-intrinsic-size: 1px 300px;
}
/* Optimize Carousels (viewport only) */
.a-carousel-viewport {
content-visibility: auto;
contain-intrinsic-size: 1px 250px;
}
/* Defer Footer */
#navFooter, .nav-footer-line {
content-visibility: auto;
contain-intrinsic-size: 1px 500px;
}
/* Reduce Spinner CPU Usage */
.a-spinner-wrapper, .loading-spinner {
will-change: transform, opacity;
}
/* GPU Acceleration for Search Images */
img.s-image {
transform: translateZ(0);
}
`;
if (typeof GM_addStyle === 'function') {
GM_addStyle(css);
} else {
const style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
}
}
/**
* 3. Background Throttler
* Wraps timing functions to throttle them dynamically based on visibility state.
* FIXED: Removed the logic that deleted the wrappers on visibility change.
*/
function initializeThrottler() {
if (window.self !== window.top) return; // Skip iframes
const originalSetInterval = window.setInterval;
const originalRequestAnimationFrame = window.requestAnimationFrame;
// Throttle Intervals
window.setInterval = function(callback, delay, ...args) {
// If hidden and delay is aggressive, force it to be slower
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 (RAF)
window.requestAnimationFrame = function(callback) {
if (document.hidden && CONFIG.DISABLE_ANIMATION_IN_BACKGROUND) {
// Delay execution to 1 second (1 FPS)
return setTimeout(() => {
originalRequestAnimationFrame(callback);
}, 1000);
}
return originalRequestAnimationFrame(callback);
};
}
/**
* 4. Resource Optimizer
* Manages lazy loading and fetch priority without expensive DOM queries.
*/
function optimizeResources(rootNode = document) {
// Select only unoptimized images
const images = rootNode.querySelectorAll('img:not([data-optimized])');
if (images.length === 0) return;
// Performance: Avoid querying the DOM inside the loop.
// We use a heuristic: If the image is physically near the top of the page, it's critical.
for (let i = 0, len = images.length; i < len; i++) {
const img = images[i];
// Mark processed immediately
img.dataset.optimized = 'true';
// A. Lazy Loading & Async Decoding
if (!img.hasAttribute('loading')) img.loading = 'lazy';
if (!img.hasAttribute('decoding')) img.decoding = 'async';
// B. Priority Management
if (SUPPORTS.fetchPriority) {
// Lower priority for footer
if (img.closest('#navFooter')) {
img.fetchPriority = 'low';
continue;
}
// High priority for "above the fold" product images
// We use a loose coordinate check rather than calculating DOM index (much faster)
if (img.classList.contains('s-image')) {
// Check if image is roughly in the top viewport
const rect = img.getBoundingClientRect();
// If top of image is within defined threshold (e.g., 1200px)
const isAboveFold = rect.top < CONFIG.CRITICAL_VIEWPORT_HEIGHT;
img.fetchPriority = isAboveFold ? 'high' : 'auto';
}
}
}
// Optimize Iframes (Ads/Tracking)
const iframes = rootNode.querySelectorAll('iframe:not([loading])');
for (let i = 0; i < iframes.length; i++) {
iframes[i].loading = 'lazy';
}
}
/**
* 5. Initialization & Observer
*/
function main() {
console.log('[Amazon Smoother] Initializing optimized v0.0.9...');
try {
injectOptimizedStyles();
initializeThrottler();
// Initial pass
if (SUPPORTS.requestIdleCallback) {
requestIdleCallback(() => optimizeResources(document), { timeout: 2000 });
} else {
optimizeResources(document);
}
// Debounced Observer
let debounceTimer;
const observer = new MutationObserver((mutations) => {
// Quick check if relevant nodes were added
let hasNewNodes = false;
for (const m of mutations) {
if (m.addedNodes.length > 0) {
hasNewNodes = true;
break;
}
}
if (!hasNewNodes) return;
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
if (SUPPORTS.requestIdleCallback) {
requestIdleCallback(() => optimizeResources(document.body));
} else {
optimizeResources(document.body);
}
}, CONFIG.DEBOUNCE_DELAY_MS);
});
// Observe the main slot if available (better performance), else body
const targetNode = document.querySelector('.s-main-slot') || document.body;
observer.observe(targetNode, {
childList: true,
subtree: true
});
} catch (error) {
console.error('[Amazon Smoother] Initialization failed:', error);
}
}
// Boot
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', main, { once: true });
} else {
main();
}
})();