// ==UserScript==
// @name Ultimate Web Optimizer
// @namespace https://gf.qytechs.cn/zh-CN/users/1474228-moyu001
// @version 2.1
// @description 全面的网页性能优化方案- 懒加载/预加载/预连接/布局优化
// @author moyu001
// @match *://*/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_log
// @license MIT
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
// ========================
// 工具函数 - 提前定义
// ========================
/**
* 防抖函数
* @param {Function} fn 要防抖的函数
* @param {number} delay 延迟时间
* @returns {Function} 防抖后的函数
*/
function debounce(fn, delay) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
/**
* 节流函数
* @param {Function} func 要节流的函数
* @param {number} limit 限制时间
* @returns {Function} 节流后的函数
*/
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
/**
* 安全的 URL 解析
* @param {string} url URL 字符串
* @param {string} base 基准 URL
* @returns {URL|null} URL 对象或 null
*/
function safeParseURL(url, base) {
try {
return new URL(url, base);
} catch {
return null;
}
}
/**
* 检查元素是否可见
* @param {Element} element 要检查的元素
* @returns {boolean} 是否可见
*/
function isElementVisible(element) {
if (!element) return false;
const style = window.getComputedStyle(element);
return style.display !== 'none' &&
style.visibility !== 'hidden' &&
style.opacity !== '0';
}
/**
* 深度合并对象
* @param {Object} target 目标对象
* @param {Object} source 源对象
* @returns {Object} 合并后的对象
*/
function deepMerge(target, source) {
const result = { ...target };
for (const key in source) {
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
result[key] = deepMerge(result[key] || {}, source[key]);
} else {
result[key] = source[key];
}
}
return result;
}
/**
* 检查是否为 HTML 图片元素 - 增强类型检查
* @param {Node} node DOM 节点
* @returns {boolean} 是否为图片元素
*/
function isImageElement(node) {
return node instanceof HTMLImageElement;
}
/**
* 延迟函数
* @param {number} ms 延迟毫秒数
* @returns {Promise} Promise 对象
*/
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* 简单的 LRU 缓存实现
*/
class LRUCache {
constructor(maxSize = 100) {
this.maxSize = maxSize;
this.cache = new Map();
}
get(key) {
if (this.cache.has(key)) {
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
return null;
}
set(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key);
} else if (this.cache.size >= this.maxSize) {
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, value);
}
has(key) {
return this.cache.has(key);
}
clear() {
this.cache.clear();
}
get size() {
return this.cache.size;
}
}
/**
* 重试操作工具类 - 新增错误重试机制
*/
class RetryableOperation {
/**
* 执行带重试的操作
* @param {Function} operation 要执行的操作
* @param {number} maxRetries 最大重试次数
* @param {number} baseDelay 基础延迟时间
* @returns {Promise} 操作结果
*/
static async executeWithRetry(operation, maxRetries = 3, baseDelay = 1000) {
for (let i = 0; i < maxRetries; i++) {
try {
return await operation();
} catch (e) {
if (i === maxRetries - 1) throw e;
await delay(baseDelay * (i + 1));
}
}
}
}
/**
* 性能监控器
*/
class PerformanceMonitor {
constructor(debug = false) {
this.debug = debug;
this.metrics = new Map();
this.counters = new Map();
}
start(name) {
if (this.debug) {
this.metrics.set(name, performance.now());
}
}
end(name) {
if (this.debug && this.metrics.has(name)) {
const duration = performance.now() - this.metrics.get(name);
console.log(`[性能] ${name}: ${duration.toFixed(2)}ms`);
this.metrics.delete(name);
return duration;
}
return 0;
}
count(name) {
if (this.debug) {
this.counters.set(name, (this.counters.get(name) || 0) + 1);
}
}
getCounter(name) {
return this.counters.get(name) || 0;
}
profile(name, fn) {
if (!this.debug) return fn();
const start = performance.now();
const result = fn();
const end = performance.now();
console.log(`[性能] ${name}: ${(end - start).toFixed(2)}ms`);
return result;
}
log(message, ...args) {
if (this.debug) {
console.log(`[优化器] ${message}`, ...args);
}
}
warn(message, ...args) {
if (this.debug) {
console.warn(`[优化器] ${message}`, ...args);
}
}
error(message, ...args) {
if (this.debug) {
console.error(`[优化器] ${message}`, ...args);
}
}
}
// ========================
// 配置管理系统
// ========================
/**
* 配置管理器 - 使用深度合并
*/
class ConfigManager {
constructor() {
this.defaultConfig = {
debug: false,
// 全局黑名单
globalBlacklist: [
// 可以添加需要完全跳过优化的域名
],
// 懒加载配置
lazyLoad: {
enabled: true,
minSize: 100,
rootMargin: '200px',
threshold: 0.01,
skipHidden: true,
batchSize: 32,
blacklist: []
},
// 预连接配置
preconnect: {
enabled: true,
maxConnections: 5,
whitelist: [
// CDN 和字体服务
'fonts.gstatic.com',
'fonts.googleapis.com',
'fonts.googleapis.cn',
'fonts.loli.net',
// 常用 CDN
'cdnjs.cloudflare.com',
'cdn.jsdelivr.net',
'unpkg.com',
'cdn.bootcdn.net',
'cdn.bootcss.com',
'libs.baidu.com',
'cdn.staticfile.org',
// 其他常用服务
'ajax.googleapis.com',
'code.jquery.com',
'maxcdn.bootstrapcdn.com',
'kit.fontawesome.com',
'lf3-cdn-tos.bytecdntp.com',
'unpkg.zhimg.com',
'npm.elemecdn.com',
'g.alicdn.com'
],
blacklist: []
},
// 预加载配置
preload: {
enabled: true,
maxPreloads: 5,
types: ['css', 'js', 'woff2', 'woff'],
fetchTimeout: 5000,
retryAttempts: 3,
blacklist: []
},
// 布局稳定性配置
layout: {
enabled: true,
stableImages: true,
stableIframes: true,
blacklist: []
}
};
this.config = this.loadConfig();
this.validateConfig();
}
loadConfig() {
try {
const saved = GM_getValue('optimizer_config_v2', null);
if (saved) {
// 使用深度合并替代浅合并
return deepMerge(this.defaultConfig, JSON.parse(saved));
}
} catch (e) {
console.warn('[配置] 加载用户配置失败,使用默认配置', e);
}
return deepMerge({}, this.defaultConfig);
}
saveConfig() {
try {
GM_setValue('optimizer_config_v2', JSON.stringify(this.config));
} catch (e) {
console.warn('[配置] 保存配置失败', e);
}
}
validateConfig() {
// 基本类型验证
if (typeof this.config.debug !== 'boolean') {
this.config.debug = this.defaultConfig.debug;
}
// 确保数组类型配置正确
['globalBlacklist'].forEach(key => {
if (!Array.isArray(this.config[key])) {
this.config[key] = [...this.defaultConfig[key]];
}
});
// 验证子配置
['lazyLoad', 'preconnect', 'preload', 'layout'].forEach(module => {
if (!this.config[module] || typeof this.config[module] !== 'object') {
this.config[module] = deepMerge({}, this.defaultConfig[module]);
}
});
}
get(path) {
const keys = path.split('.');
let value = this.config;
for (const key of keys) {
value = value?.[key];
if (value === undefined) break;
}
return value;
}
set(path, value) {
const keys = path.split('.');
const lastKey = keys.pop();
let target = this.config;
for (const key of keys) {
if (!target[key] || typeof target[key] !== 'object') {
target[key] = {};
}
target = target[key];
}
target[lastKey] = value;
this.saveConfig();
}
isBlacklisted(hostname, module = null) {
// 检查全局黑名单
if (this.config.globalBlacklist.some(domain => hostname.includes(domain))) {
return true;
}
// 检查模块特定黑名单
if (module && this.config[module]?.blacklist) {
return this.config[module].blacklist.some(domain => hostname.includes(domain));
}
return false;
}
}
// ========================
// 核心优化模块
// ========================
/**
* 懒加载管理器 - 增强类型检查
*/
class LazyLoadManager {
constructor(config, monitor) {
this.config = config;
this.monitor = monitor;
this.observer = null;
this.mutationObserver = null;
this.processedImages = new Set();
this.pendingImages = [];
this.batchScheduled = false;
this.processedElements = new WeakSet(); // 避免重复处理
}
init() {
if (!this.config.get('lazyLoad.enabled')) {
this.monitor.log('懒加载功能已禁用');
return;
}
if (this.config.isBlacklisted(location.hostname, 'lazyLoad')) {
this.monitor.log('当前站点在懒加载黑名单中');
return;
}
this.monitor.start('lazyLoad-init');
this.setupIntersectionObserver();
this.processExistingImages();
this.setupMutationObserver();
this.monitor.end('lazyLoad-init');
}
setupIntersectionObserver() {
if (!('IntersectionObserver' in window)) {
this.monitor.warn('浏览器不支持 IntersectionObserver,使用兼容模式');
this.setupFallbackMode();
return;
}
this.observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.restoreImage(entry.target);
this.observer.unobserve(entry.target);
this.monitor.count('lazy-loaded-images');
}
});
}, {
rootMargin: this.config.get('lazyLoad.rootMargin'),
threshold: this.config.get('lazyLoad.threshold')
});
}
setupFallbackMode() {
const checkVisible = throttle(() => {
const images = document.querySelectorAll('img[data-lazy-src]');
const margin = parseInt(this.config.get('lazyLoad.rootMargin')) || 200;
images.forEach(img => {
const rect = img.getBoundingClientRect();
if (rect.top < window.innerHeight + margin) {
this.restoreImage(img);
}
});
}, 200);
window.addEventListener('scroll', checkVisible, { passive: true });
window.addEventListener('resize', checkVisible, { passive: true });
checkVisible();
}
isLazyCandidate(img) {
// 基本检查 - 使用更严格的类型检查
if (!isImageElement(img)) return false;
if (this.processedElements.has(img)) return false;
if (img.hasAttribute('data-lazy-processed')) return false;
if (img.loading === 'eager') return false;
if (img.complete && img.src) return false;
if (img.src && img.src.startsWith('data:')) return false;
// 跳过已有懒加载的图片
if (img.hasAttribute('data-src') || img.hasAttribute('data-srcset')) return false;
// 尺寸检查
const minSize = this.config.get('lazyLoad.minSize');
const rect = img.getBoundingClientRect();
if (rect.width < minSize || rect.height < minSize) return false;
// 可见性检查
if (this.config.get('lazyLoad.skipHidden') && !isElementVisible(img)) {
return false;
}
return true;
}
processImage(img) {
if (!this.isLazyCandidate(img)) return false;
// 标记为已处理
this.processedElements.add(img);
img.setAttribute('data-lazy-processed', 'true');
// 设置原生懒加载(如果支持)
if ('loading' in HTMLImageElement.prototype) {
img.loading = 'lazy';
}
// 保存原始 src
if (img.src) {
img.setAttribute('data-lazy-src', img.src);
img.removeAttribute('src');
}
if (img.srcset) {
img.setAttribute('data-lazy-srcset', img.srcset);
img.removeAttribute('srcset');
}
// 添加到观察者
if (this.observer) {
this.observer.observe(img);
}
this.processedImages.add(img);
this.monitor.count('processed-images');
return true;
}
restoreImage(img) {
const src = img.getAttribute('data-lazy-src');
const srcset = img.getAttribute('data-lazy-srcset');
if (src) {
img.src = src;
img.removeAttribute('data-lazy-src');
}
if (srcset) {
img.srcset = srcset;
img.removeAttribute('data-lazy-srcset');
}
this.processedImages.delete(img);
}
batchProcess(images) {
const batchSize = this.config.get('lazyLoad.batchSize');
let processed = 0;
const processBatch = () => {
const end = Math.min(processed + batchSize, images.length);
for (let i = processed; i < end; i++) {
this.processImage(images[i]);
}
processed = end;
if (processed < images.length) {
(window.requestIdleCallback || window.requestAnimationFrame)(processBatch);
} else {
this.monitor.log(`懒加载处理完成,共处理 ${processed} 张图片`);
}
};
processBatch();
}
processExistingImages() {
const images = Array.from(document.querySelectorAll('img'));
this.monitor.log(`发现 ${images.length} 张图片,开始批量处理`);
this.batchProcess(images);
}
// 改进的批处理调度 - 更好的并发控制
scheduleBatchProcess() {
if (this.batchScheduled || this.pendingImages.length === 0) return;
this.batchScheduled = true;
(window.requestIdleCallback || window.requestAnimationFrame)(() => {
const images = [...this.pendingImages];
this.pendingImages = [];
this.batchScheduled = false;
let processedCount = 0;
images.forEach(img => {
if (this.processImage(img)) {
processedCount++;
}
});
if (processedCount > 0) {
this.monitor.log(`动态处理 ${processedCount} 张新图片`);
}
});
}
setupMutationObserver() {
let pendingMutations = [];
let processingScheduled = false;
const processMutations = () => {
const mutations = [...pendingMutations];
pendingMutations = [];
processingScheduled = false;
mutations.forEach(mutation => {
// 处理新增节点
mutation.addedNodes.forEach(node => {
if (isImageElement(node)) {
this.pendingImages.push(node);
} else if (node.querySelectorAll) {
const images = node.querySelectorAll('img');
this.pendingImages.push(...Array.from(images));
}
});
// 清理移除的节点
mutation.removedNodes.forEach(node => {
if (isImageElement(node) && this.observer) {
this.observer.unobserve(node);
this.processedImages.delete(node);
this.processedElements.delete && this.processedElements.delete(node);
}
});
});
this.scheduleBatchProcess();
};
this.mutationObserver = new MutationObserver((mutations) => {
pendingMutations.push(...mutations);
if (!processingScheduled) {
processingScheduled = true;
(window.requestIdleCallback || window.requestAnimationFrame)(processMutations);
}
});
this.mutationObserver.observe(document.body, {
childList: true,
subtree: true
});
}
destroy() {
if (this.observer) {
this.observer.disconnect();
this.observer = null;
}
if (this.mutationObserver) {
this.mutationObserver.disconnect();
this.mutationObserver = null;
}
// 恢复所有处理过的图片
this.processedImages.forEach(img => this.restoreImage(img));
this.processedImages.clear();
}
}
/**
* 预加载管理器 - 改进异步处理
*/
class PreloadManager {
constructor(config, monitor) {
this.config = config;
this.monitor = monitor;
this.preloaded = new LRUCache(this.config.get('preload.maxPreloads'));
this.cssCache = new LRUCache(50);
this.mutationObserver = null;
}
async init() {
if (!this.config.get('preload.enabled')) {
this.monitor.log('预加载功能已禁用');
return;
}
if (this.config.isBlacklisted(location.hostname, 'preload')) {
this.monitor.log('当前站点在预加载黑名单中');
return;
}
this.monitor.start('preload-init');
await this.scanExistingResources(); // 改为异步
this.setupMutationObserver();
this.monitor.end('preload-init');
}
getResourceType(url) {
const ext = url.split('.').pop()?.toLowerCase();
const types = this.config.get('preload.types');
if (!types.includes(ext)) return null;
switch (ext) {
case 'css': return 'style';
case 'js': return 'script';
case 'woff':
case 'woff2':
case 'ttf':
case 'otf': return 'font';
default: return null;
}
}
doPreload(url, asType) {
if (this.preloaded.has(url) || this.preloaded.size >= this.config.get('preload.maxPreloads')) {
return false;
}
try {
const link = document.createElement('link');
link.rel = 'preload';
link.as = asType;
link.href = url;
if (asType === 'font') {
link.crossOrigin = 'anonymous';
// 设置正确的 MIME 类型
if (url.includes('.woff2')) link.type = 'font/woff2';
else if (url.includes('.woff')) link.type = 'font/woff';
else if (url.includes('.ttf')) link.type = 'font/ttf';
else if (url.includes('.otf')) link.type = 'font/otf';
}
document.head.appendChild(link);
this.preloaded.set(url, true);
this.monitor.log(`预加载 ${asType}: ${url}`);
this.monitor.count('preloaded-resources');
return true;
} catch (e) {
this.monitor.warn(`预加载失败: ${url}`, e);
return false;
}
}
async extractFontsFromCSS(cssUrl) {
if (this.cssCache.has(cssUrl)) {
return this.cssCache.get(cssUrl);
}
const operation = async () => {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.config.get('preload.fetchTimeout'));
try {
const response = await fetch(cssUrl, {
signal: controller.signal,
mode: 'cors',
credentials: 'omit'
});
clearTimeout(timeoutId);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const text = await response.text();
const fontUrls = [];
const fontRegex = /url\(["']?([^")']+\.(woff2?|ttf|otf))["']?\)/gi;
let match;
while ((match = fontRegex.exec(text)) !== null) {
const fontUrl = safeParseURL(match[1], cssUrl);
if (fontUrl) {
fontUrls.push(fontUrl.href);
}
}
this.cssCache.set(cssUrl, fontUrls);
return fontUrls;
} finally {
clearTimeout(timeoutId);
}
};
try {
// 使用重试机制
return await RetryableOperation.executeWithRetry(
operation,
this.config.get('preload.retryAttempts')
);
} catch (e) {
this.monitor.warn(`提取字体失败: ${cssUrl}`, e.message);
this.cssCache.set(cssUrl, []);
return [];
}
}
// 改进的异步资源扫描 - 更好的并发控制
async scanExistingResources() {
// 处理 CSS 文件
const cssLinks = Array.from(document.querySelectorAll('link[rel="stylesheet"][href]'));
const jsScripts = Array.from(document.querySelectorAll('script[src]'));
// 处理 CSS 文件的 Promise 数组
const cssPromises = cssLinks.map(async link => {
const cssUrl = link.href;
const asType = this.getResourceType(cssUrl);
if (asType === 'style') {
this.doPreload(cssUrl, asType);
// 异步提取和预加载字体
try {
const fontUrls = await this.extractFontsFromCSS(cssUrl);
fontUrls.forEach(fontUrl => {
const fontType = this.getResourceType(fontUrl);
if (fontType === 'font') {
this.doPreload(fontUrl, fontType);
}
});
} catch (e) {
// 忽略字体提取错误,不影响主流程
this.monitor.warn(`CSS处理失败: ${cssUrl}`, e);
}
}
});
// 处理 JS 文件
jsScripts.forEach(script => {
const asType = this.getResourceType(script.src);
if (asType === 'script') {
this.doPreload(script.src, asType);
}
});
// 等待所有 CSS 处理完成,但不阻塞初始化
try {
await Promise.allSettled(cssPromises);
this.monitor.log(`资源扫描完成,处理了 ${cssLinks.length} 个CSS文件和 ${jsScripts.length} 个JS文件`);
} catch (e) {
this.monitor.warn('资源扫描过程中出现错误', e);
}
}
setupMutationObserver() {
this.mutationObserver = new MutationObserver(debounce(async (mutations) => {
const newCSSLinks = [];
const newJSScripts = [];
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.tagName === 'LINK' && node.rel === 'stylesheet' && node.href) {
newCSSLinks.push(node);
} else if (node.tagName === 'SCRIPT' && node.src) {
newJSScripts.push(node);
}
});
});
// 异步处理新添加的资源
if (newCSSLinks.length > 0 || newJSScripts.length > 0) {
const promises = newCSSLinks.map(async node => {
const asType = this.getResourceType(node.href);
if (asType === 'style') {
this.doPreload(node.href, asType);
// 异步处理字体
try {
const fontUrls = await this.extractFontsFromCSS(node.href);
fontUrls.forEach(fontUrl => {
const fontType = this.getResourceType(fontUrl);
if (fontType === 'font') {
this.doPreload(fontUrl, fontType);
}
});
} catch (e) {
// 忽略错误
}
}
});
newJSScripts.forEach(node => {
const asType = this.getResourceType(node.src);
if (asType === 'script') {
this.doPreload(node.src, asType);
}
});
// 不等待 Promise 完成,避免阻塞
Promise.allSettled(promises).then(() => {
this.monitor.log(`动态处理了 ${newCSSLinks.length} 个CSS和 ${newJSScripts.length} 个JS`);
});
}
}, 200));
this.mutationObserver.observe(document.body, {
childList: true,
subtree: true
});
}
destroy() {
if (this.mutationObserver) {
this.mutationObserver.disconnect();
this.mutationObserver = null;
}
this.preloaded.clear();
this.cssCache.clear();
}
}
// 其他管理器类保持不变,仅引用已改进的配置和监控器
class PreconnectManager {
constructor(config, monitor) {
this.config = config;
this.monitor = monitor;
this.connected = new LRUCache(this.config.get('preconnect.maxConnections'));
this.mutationObserver = null;
}
init() {
if (!this.config.get('preconnect.enabled')) {
this.monitor.log('预连接功能已禁用');
return;
}
if (this.config.isBlacklisted(location.hostname, 'preconnect')) {
this.monitor.log('当前站点在预连接黑名单中');
return;
}
this.monitor.start('preconnect-init');
this.scanExistingResources();
this.setupMutationObserver();
this.monitor.end('preconnect-init');
}
shouldPreconnect(hostname) {
if (!hostname || hostname === location.hostname) return false;
if (this.connected.has(hostname)) return false;
const whitelist = this.config.get('preconnect.whitelist');
return whitelist.some(domain => hostname.endsWith(domain));
}
doPreconnect(hostname) {
if (!this.shouldPreconnect(hostname)) return false;
try {
const link = document.createElement('link');
link.rel = 'preconnect';
link.href = `https://${hostname}`;
link.crossOrigin = 'anonymous';
document.head.appendChild(link);
this.connected.set(hostname, true);
this.monitor.log(`预连接: ${hostname}`);
this.monitor.count('preconnected-domains');
return true;
} catch (e) {
this.monitor.warn(`预连接失败: ${hostname}`, e);
return false;
}
}
extractHostnames(elements) {
const hostnames = new Set();
elements.forEach(el => {
const url = safeParseURL(el.src || el.href);
if (url && url.hostname !== location.hostname) {
hostnames.add(url.hostname);
}
});
return Array.from(hostnames);
}
scanExistingResources() {
const selectors = [
'script[src]',
'link[href]',
'img[src]',
'audio[src]',
'video[src]',
'source[src]'
];
const elements = document.querySelectorAll(selectors.join(','));
const hostnames = this.extractHostnames(elements);
let connected = 0;
hostnames.forEach(hostname => {
if (this.doPreconnect(hostname)) {
connected++;
}
});
this.monitor.log(`扫描完成,预连接 ${connected} 个域名`);
}
setupMutationObserver() {
this.mutationObserver = new MutationObserver(debounce((mutations) => {
const newElements = [];
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.src || node.href) {
newElements.push(node);
} else if (node.querySelectorAll) {
const elements = node.querySelectorAll('script[src], link[href], img[src]');
newElements.push(...elements);
}
});
});
if (newElements.length > 0) {
const hostnames = this.extractHostnames(newElements);
hostnames.forEach(hostname => this.doPreconnect(hostname));
}
}, 200));
this.mutationObserver.observe(document.body, {
childList: true,
subtree: true
});
}
destroy() {
if (this.mutationObserver) {
this.mutationObserver.disconnect();
this.mutationObserver = null;
}
this.connected.clear();
}
}
class LayoutStabilizer {
constructor(config, monitor) {
this.config = config;
this.monitor = monitor;
this.injectedStyle = null;
}
init() {
if (!this.config.get('layout.enabled')) {
this.monitor.log('布局优化功能已禁用');
return;
}
if (this.config.isBlacklisted(location.hostname, 'layout')) {
this.monitor.log('当前站点在布局优化黑名单中');
return;
}
this.monitor.start('layout-init');
this.injectStabilizationStyles();
this.monitor.end('layout-init');
}
generateCSS() {
const styles = [];
if (this.config.get('layout.stableImages')) {
styles.push(`
/* 图片布局稳定性优化 */
img:not([width]):not([height]):not([style*="width"]):not([style*="height"]) {
min-height: 1px;
// max-width: 100%;
// height: auto;
}
/* 现代浏览器的 aspect-ratio 支持 */
@supports (aspect-ratio: 1/1) {
img[width][height]:not([style*="aspect-ratio"]) {
aspect-ratio: attr(width) / attr(height);
}
}
`);
}
if (this.config.get('layout.stableIframes')) {
styles.push(`
/* iframe 布局稳定性优化 */
iframe:not([width]):not([height]):not([style*="width"]):not([style*="height"]) {
// width: 100%;
// height: auto;
min-height: 1px;
}
@supports (aspect-ratio: 16/9) {
iframe:not([style*="aspect-ratio"]) {
aspect-ratio: 16/9;
}
}
`);
}
return styles.join('\n');
}
injectStabilizationStyles() {
const css = this.generateCSS().trim();
if (!css) return;
try {
this.injectedStyle = document.createElement('style');
this.injectedStyle.setAttribute('data-optimizer', 'layout-stabilizer');
this.injectedStyle.textContent = css;
// 优先插入到 head,如果不存在则插入到 document
const target = document.head || document.documentElement;
target.appendChild(this.injectedStyle);
this.monitor.log('布局稳定性样式已注入');
} catch (e) {
this.monitor.warn('注入布局样式失败', e);
}
}
destroy() {
if (this.injectedStyle && this.injectedStyle.parentNode) {
this.injectedStyle.parentNode.removeChild(this.injectedStyle);
this.injectedStyle = null;
this.monitor.log('布局样式已移除');
}
}
}
// ========================
// 主优化器
// ========================
/**
* 主优化器类 - v2.0
*/
class WebOptimizer {
constructor() {
this.config = new ConfigManager();
this.monitor = new PerformanceMonitor(this.config.get('debug'));
// 优化模块
this.modules = {
lazyLoad: new LazyLoadManager(this.config, this.monitor),
preconnect: new PreconnectManager(this.config, this.monitor),
preload: new PreloadManager(this.config, this.monitor),
layout: new LayoutStabilizer(this.config, this.monitor)
};
this.initialized = false;
this.cleanupTasks = [];
}
async init() {
if (this.initialized) return;
this.monitor.start('total-init');
this.monitor.log('Web Optimizer Enhanced v2.0 开始初始化');
// 检查全局黑名单
if (this.config.isBlacklisted(location.hostname)) {
this.monitor.log('当前站点在全局黑名单中,跳过所有优化');
return;
}
try {
// 等待 DOM 基本可用
if (document.readyState === 'loading') {
await new Promise(resolve => {
document.addEventListener('DOMContentLoaded', resolve, { once: true });
});
}
// 初始化各个模块 - 改进的错误隔离
const initPromises = Object.entries(this.modules).map(async ([name, module]) => {
try {
this.monitor.start(`init-${name}`);
await module.init();
this.monitor.end(`init-${name}`);
return { name, success: true };
} catch (e) {
this.monitor.error(`模块 ${name} 初始化失败`, e);
return { name, success: false, error: e };
}
});
const results = await Promise.allSettled(initPromises);
const successCount = results.filter(r => r.status === 'fulfilled' && r.value.success).length;
this.monitor.log(`模块初始化完成,成功: ${successCount}/${Object.keys(this.modules).length}`);
// 设置清理任务
this.setupCleanupTasks();
this.initialized = true;
this.monitor.end('total-init');
this.monitor.log('Web Optimizer Enhanced v2.0 初始化完成');
// 显示初始化报告
this.showInitReport();
} catch (e) {
this.monitor.error('初始化失败', e);
}
}
setupCleanupTasks() {
// 定期清理缓存
const cleanupInterval = setInterval(() => {
Object.values(this.modules).forEach(module => {
if (module.cssCache) module.cssCache.clear();
if (module.connected) module.connected.clear();
if (module.preloaded) module.preloaded.clear();
});
this.monitor.log('定期清理完成');
}, 10 * 60 * 1000); // 10分钟
this.cleanupTasks.push(() => clearInterval(cleanupInterval));
// 页面卸载时清理
const cleanup = () => {
this.destroy();
};
window.addEventListener('beforeunload', cleanup);
this.cleanupTasks.push(() => {
window.removeEventListener('beforeunload', cleanup);
});
}
showInitReport() {
if (!this.config.get('debug')) return;
console.groupCollapsed('[Web Optimizer Enhanced v2.0] 初始化报告');
console.log('版本: 2.0');
console.log('当前域名:', location.hostname);
console.log('启用的功能:', Object.entries(this.config.config)
.filter(([key, value]) => typeof value === 'object' && value.enabled)
.map(([key]) => key)
);
// 显示性能计数器
console.log('性能计数:', {
'processed-images': this.monitor.getCounter('processed-images'),
'lazy-loaded-images': this.monitor.getCounter('lazy-loaded-images'),
'preconnected-domains': this.monitor.getCounter('preconnected-domains'),
'preloaded-resources': this.monitor.getCounter('preloaded-resources')
});
console.log('配置详情:', this.config.config);
console.groupEnd();
}
destroy() {
if (!this.initialized) return;
this.monitor.log('开始清理资源');
// 清理各个模块
Object.values(this.modules).forEach(module => {
if (module.destroy) {
try {
module.destroy();
} catch (e) {
this.monitor.warn('模块清理失败', e);
}
}
});
// 执行清理任务
this.cleanupTasks.forEach(task => {
try {
task();
} catch (e) {
this.monitor.warn('清理任务执行失败', e);
}
});
this.cleanupTasks = [];
this.initialized = false;
this.monitor.log('资源清理完成');
}
// 公共 API - 增强版
updateConfig(path, value) {
this.config.set(path, value);
this.monitor.log(`配置已更新: ${path} = ${value}`);
// 如果是调试模式变更,更新监控器
if (path === 'debug') {
this.monitor.debug = value;
}
}
getStats() {
return {
initialized: this.initialized,
version: '2.0',
hostname: location.hostname,
config: this.config.config,
modules: Object.keys(this.modules),
counters: {
'processed-images': this.monitor.getCounter('processed-images'),
'lazy-loaded-images': this.monitor.getCounter('lazy-loaded-images'),
'preconnected-domains': this.monitor.getCounter('preconnected-domains'),
'preloaded-resources': this.monitor.getCounter('preloaded-resources')
}
};
}
// 新增:性能报告
getPerformanceReport() {
const stats = this.getStats();
const imageStats = {
total: document.querySelectorAll('img').length,
processed: stats.counters['processed-images'],
lazyLoaded: stats.counters['lazy-loaded-images']
};
return {
...stats,
performance: {
images: imageStats,
domains: stats.counters['preconnected-domains'],
resources: stats.counters['preloaded-resources'],
efficiency: imageStats.total > 0 ? (imageStats.processed / imageStats.total * 100).toFixed(1) + '%' : '0%'
}
};
}
}
// ========================
// 全局初始化
// ========================
// 创建全局实例
const optimizer = new WebOptimizer();
// 暴露到全局作用域(调试用)
if (typeof window !== 'undefined') {
window.WebOptimizer = optimizer;
// v2.0 新增:暴露工具函数供调试使用
window.WebOptimizerUtils = {
debounce,
throttle,
safeParseURL,
isElementVisible,
deepMerge,
delay,
RetryableOperation
};
}
// 启动优化器
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => optimizer.init());
} else {
// 延迟一点时间,确保页面基本稳定
setTimeout(() => optimizer.init(), 100);
}
})();