Files
SkyArtShop/website/public/assets/js/resource-optimizer.js

249 lines
6.8 KiB
JavaScript
Raw Normal View History

2026-01-04 17:52:37 -06:00
/**
* Resource Preloading and Optimization Manager
* Manages critical resource loading and performance optimization
*/
(function () {
"use strict";
const ResourceOptimizer = {
// Preload critical resources
preloadCritical() {
const criticalResources = [
{ href: "/assets/css/main.css", as: "style" },
{ href: "/assets/css/navbar.css", as: "style" },
{ href: "/assets/js/main.js", as: "script" },
];
criticalResources.forEach((resource) => {
const link = document.createElement("link");
link.rel = "preload";
link.href = resource.href;
link.as = resource.as;
if (resource.as === "style") {
link.onload = function () {
this.rel = "stylesheet";
};
}
document.head.appendChild(link);
});
},
// Prefetch resources for likely navigation
prefetchRoutes() {
const routes = [
"/shop.html",
"/product.html",
"/about.html",
"/contact.html",
];
// Prefetch on idle
if ("requestIdleCallback" in window) {
requestIdleCallback(() => {
routes.forEach((route) => {
const link = document.createElement("link");
link.rel = "prefetch";
link.href = route;
document.head.appendChild(link);
});
});
}
},
// Defer non-critical scripts
deferNonCritical() {
const scripts = document.querySelectorAll("script[data-defer]");
const loadScript = (script) => {
const newScript = document.createElement("script");
newScript.src = script.dataset.defer;
newScript.async = true;
document.body.appendChild(newScript);
};
if ("requestIdleCallback" in window) {
scripts.forEach((script) => {
requestIdleCallback(() => loadScript(script));
});
} else {
setTimeout(() => {
scripts.forEach(loadScript);
}, 2000);
}
},
// Optimize font loading
optimizeFonts() {
// Use font-display: swap for all fonts
if (document.fonts && document.fonts.ready) {
document.fonts.ready.then(() => {
document.body.classList.add("fonts-loaded");
});
}
},
// Reduce main thread work with requestIdleCallback
scheduleIdleWork(callback) {
if ("requestIdleCallback" in window) {
requestIdleCallback(callback, { timeout: 2000 });
} else {
setTimeout(callback, 1);
}
},
// Batch DOM reads and writes
batchDOMOperations(operations) {
requestAnimationFrame(() => {
operations.forEach((op) => op());
});
},
// Monitor performance
monitorPerformance() {
if ("PerformanceObserver" in window) {
// Monitor Long Tasks
try {
const longTaskObserver = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 50) {
console.warn("Long task detected:", {
duration: entry.duration,
startTime: entry.startTime,
});
}
}
});
longTaskObserver.observe({ entryTypes: ["longtask"] });
} catch (e) {
// Long task API not supported
}
// Monitor Largest Contentful Paint
try {
const lcpObserver = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
console.log("LCP:", lastEntry.renderTime || lastEntry.loadTime);
});
lcpObserver.observe({ entryTypes: ["largest-contentful-paint"] });
} catch (e) {
// LCP API not supported
}
}
},
// Get performance metrics
getMetrics() {
if (!window.performance || !window.performance.timing) {
return null;
}
const timing = window.performance.timing;
const navigation = window.performance.navigation;
return {
// Page load metrics
domContentLoaded:
timing.domContentLoadedEventEnd - timing.navigationStart,
loadComplete: timing.loadEventEnd - timing.navigationStart,
// Network metrics
dns: timing.domainLookupEnd - timing.domainLookupStart,
tcp: timing.connectEnd - timing.connectStart,
request: timing.responseStart - timing.requestStart,
response: timing.responseEnd - timing.responseStart,
// Rendering metrics
domProcessing: timing.domComplete - timing.domLoading,
domInteractive: timing.domInteractive - timing.navigationStart,
// Navigation type
navigationType: navigation.type,
redirectCount: navigation.redirectCount,
};
},
// Log metrics to console (development only)
logMetrics() {
window.addEventListener("load", () => {
setTimeout(() => {
const metrics = this.getMetrics();
if (metrics) {
console.table(metrics);
}
}, 0);
});
},
// Optimize images
optimizeImages() {
const images = document.querySelectorAll("img:not([loading])");
images.forEach((img) => {
// Add native lazy loading
if ("loading" in HTMLImageElement.prototype) {
img.loading = "lazy";
}
// Add decoding hint
img.decoding = "async";
});
},
// Preconnect to external domains
preconnectDomains() {
const domains = [
"https://fonts.googleapis.com",
"https://fonts.gstatic.com",
"https://cdn.jsdelivr.net",
];
domains.forEach((domain) => {
const link = document.createElement("link");
link.rel = "preconnect";
link.href = domain;
link.crossOrigin = "anonymous";
document.head.appendChild(link);
});
},
// Initialize all optimizations
init() {
// Preconnect to external domains early
this.preconnectDomains();
// Optimize fonts
this.optimizeFonts();
// Optimize existing images
this.optimizeImages();
// Monitor performance in development
if (
window.location.hostname === "localhost" ||
window.location.hostname === "127.0.0.1"
) {
this.monitorPerformance();
this.logMetrics();
}
// Defer non-critical resources
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => {
this.deferNonCritical();
this.prefetchRoutes();
});
} else {
this.deferNonCritical();
this.prefetchRoutes();
}
},
};
// Auto-initialize
ResourceOptimizer.init();
// Export globally
window.ResourceOptimizer = ResourceOptimizer;
})();