您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
全面的网页性能优化方案(含懒加载/预加载/预连接)
当前为
// ==UserScript== // @name Ultimate Web Optimizer // @namespace https://greasyfork.org/zh-CN/users/1474228-moyu001 // @version 1.2.1 // @description 全面的网页性能优化方案(含懒加载/预加载/预连接) // @author moyu001 // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @grant GM_log // @license MIT // @run-at document-start // ==/UserScript== // 防debugger拦截(需尽早注入) (function() { // 拦截 Function 构造器 const origFunc = Function.prototype.constructor; Function.prototype.constructor = function(...args) { const code = args.join(','); if (code.includes('debugger')) { return origFunc.call(this, code.replace(/debugger;?/g, '')); } return origFunc.apply(this, args); }; // 拦截 eval const origEval = window.eval; window.eval = function(code) { if (typeof code === 'string' && code.includes('debugger')) { code = code.replace(/debugger;?/g, ''); } return origEval(code); }; })(); (function() { 'use strict'; // ======================== // 配置中心 // ======================== // 如需持久化配置,可结合GM_setValue/GM_getValue实现用户自定义 const config = { debug: false, // 开启调试日志 features: { lazyLoad: { // 图片懒加载 enabled: true, minSize: 100, // 最小处理尺寸(px) rootMargin: '200px', // 提前加载距离 skipHidden: true // 跳过隐藏图片 }, preconnect: { // 智能预连接 enabled: true, whitelist: [ // 中国大陆优化白名单 'fonts.gstatic.com', 'cdnjs.cloudflare.com', 'unpkg.com', 'ajax.googleapis.com', 'maxcdn.bootstrapcdn.com', 'code.jquery.com', 'kit.fontawesome.com', 'fonts.googleapis.cn', 'fonts.loli.net', 'cdn.jsdelivr.net', 'cdn.bootcdn.net', 'cdn.bootcss.com', 'libs.baidu.com', 'cdn.staticfile.org', 'lf3-cdn-tos.bytecdntp.com', 'unpkg.zhimg.com', 'npm.elemecdn.com', 'g.alicdn.com' ], maxConnections: 5 // 最大预连接数 }, preload: { // 资源预加载 enabled: true, types: ['css', 'js', 'woff2'], // 预加载类型 maxPreloads: 5 // 最大预加载数 }, layout: { // 布局稳定性 stableImages: true, stableIframes: true } } }; // 全局黑名单(可扩展) const optimizerSiteBlacklist = [ // 'example.com', // 可添加其它不希望优化的站点 ]; // 各模块独立黑名单(可扩展) const lazyLoadSiteBlacklist = [ // 'example.com', ]; const preconnectSiteBlacklist = [ // 'example.com', ]; const preloadSiteBlacklist = [ // 'example.com', ]; const layoutSiteBlacklist = [ // 'example.com', ]; // ======================== // 核心优化模块 // ======================== // 防抖工具 function debounce(fn, delay) { let timer = null; return function(...args) { clearTimeout(timer); timer = setTimeout(() => fn.apply(this, args), delay); }; } // 简单LRU缓存实现 class LRUSet { constructor(limit) { this.limit = limit; this.map = new Map(); } has(key) { return this.map.has(key); } add(key) { if (this.map.has(key)) { this.map.delete(key); } this.map.set(key, true); if (this.map.size > this.limit) { // 删除最早的 const firstKey = this.map.keys().next().value; this.map.delete(firstKey); } } get size() { return this.map.size; } } // 定期清理工具 function scheduleCleanup(fn, interval = 10 * 60 * 1000) { // 默认10分钟 setInterval(fn, interval); } // 图片/视频懒加载系统 const initLazyLoad = () => { if (optimizerSiteBlacklist.some(domain => location.hostname.includes(domain)) || lazyLoadSiteBlacklist.some(domain => location.hostname.includes(domain))) { if (config.debug) console.log('[懒加载] 当前站点已在全局或懒加载黑名单,跳过懒加载优化'); return; } if (!config.features.lazyLoad.enabled) return; try { let lazyCount = 0; const isLazyCandidate = (el) => { // 跳过已实现懒加载的图片(如有data-src/data-srcset属性) if (el.hasAttribute && (el.hasAttribute('data-src') || el.hasAttribute('data-srcset'))) return false; if (el.loading === 'eager') return false; if (el.complete) return false; if (el.src && el.src.startsWith('data:')) return false; if (config.features.lazyLoad.skipHidden && window.getComputedStyle(el).display === 'none') return false; const rect = el.getBoundingClientRect(); return rect.width > config.features.lazyLoad.minSize && rect.height > config.features.lazyLoad.minSize; }; const processMedia = (el) => { if ('loading' in HTMLImageElement.prototype && el.tagName === 'IMG') { el.loading = 'lazy'; } if (!el.dataset.src && el.src) { el.dataset.src = el.src; el.removeAttribute('src'); } if (el.srcset && !el.dataset.srcset) { el.dataset.srcset = el.srcset; el.removeAttribute('srcset'); } lazyCount++; return el; }; // 批量处理工具(自适应大页面) function batchProcess(arr, fn, batchSize, cb) { // batchSize自适应:图片极多时分批更小 if (!batchSize) { if (arr.length > 1000) batchSize = 8; else if (arr.length > 500) batchSize = 16; else if (arr.length > 200) batchSize = 32; else batchSize = 64; } let i = 0; function nextBatch() { for (let j = 0; j < batchSize && i < arr.length; j++, i++) { fn(arr[i]); } if (i < arr.length) { (window.requestIdleCallback || window.requestAnimationFrame)(nextBatch); } else if (cb) { cb(); } } nextBatch(); } if ('IntersectionObserver' in window) { const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const el = entry.target; if (el.dataset.src) el.src = el.dataset.src; if (el.dataset.srcset) el.srcset = el.dataset.srcset; observer.unobserve(el); } }); }, { rootMargin: config.features.lazyLoad.rootMargin, threshold: 0.01 }); // 批量observe初始图片和视频 const imgs = Array.from(document.querySelectorAll('img')).filter(isLazyCandidate); const videos = Array.from(document.querySelectorAll('video')).filter(isLazyCandidate); batchProcess(imgs.concat(videos), el => observer.observe(processMedia(el)), undefined, () => { if (config.debug) { console.log(`[懒加载] 初始图片: ${imgs.length}, 视频: ${videos.length}`); } }); // 动态加载监听,合并多次变更 let pendingMedia = []; let scheduled = false; function scheduleBatchObserve() { if (scheduled) return; scheduled = true; (window.requestIdleCallback || window.requestAnimationFrame)(() => { scheduled = false; const arr = pendingMedia; pendingMedia = []; batchProcess(arr, el => observer.observe(processMedia(el)), undefined, () => { if (config.debug && arr.length) { console.log(`[懒加载] 新增图片/视频: ${arr.length}`); } }); }); } const observeNewMedia = debounce(mutations => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if ((node.nodeName === 'IMG' || node.nodeName === 'VIDEO') && isLazyCandidate(node)) { pendingMedia.push(node); } }); }); if (pendingMedia.length) scheduleBatchObserve(); }, 50); const mo = new MutationObserver(observeNewMedia); mo.observe(document.body, { childList: true, subtree: true }); // 资源释放:监听移除节点,自动unobserve const unobserveRemoved = mutations => { mutations.forEach(mutation => { mutation.removedNodes.forEach(node => { if ((node.nodeName === 'IMG' || node.nodeName === 'VIDEO')) { observer.unobserve(node); } }); }); }; const mo2 = new MutationObserver(unobserveRemoved); mo2.observe(document.body, { childList: true, subtree: true }); } else { // 兼容模式实现 const checkVisible = throttle(() => { document.querySelectorAll('img').forEach(img => { if (isLazyCandidate(img) && !img.src) { const rect = img.getBoundingClientRect(); if (rect.top < window.innerHeight + parseInt(config.features.lazyLoad.rootMargin)) { if (img.dataset.src) img.src = img.dataset.src; if (img.dataset.srcset) img.srcset = img.dataset.srcset; } } }); }, 200); window.addEventListener('scroll', checkVisible); window.addEventListener('resize', checkVisible); // 新增resize监听 checkVisible(); } } catch (e) { if (config.debug) console.warn('[LazyLoad] 异常:', e); } }; // 智能预连接系统 const initSmartPreconnect = () => { if (optimizerSiteBlacklist.some(domain => location.hostname.includes(domain)) || preconnectSiteBlacklist.some(domain => location.hostname.includes(domain))) { if (config.debug) console.log('[预连接] 当前站点已在全局或预连接黑名单,跳过预连接优化'); return; } if (!config.features.preconnect.enabled) return; try { const processed = new LRUSet(config.features.preconnect.maxConnections); const whitelist = config.features.preconnect.whitelist; scheduleCleanup(() => { processed.map.clear && processed.map.clear(); if (config.debug) console.log('[Preconnect] processed已清理'); }, 10 * 60 * 1000); let preconnectCount = 0; const doPreconnect = (hostname) => { if (processed.has(hostname)) return; const link = document.createElement('link'); link.rel = 'preconnect'; link.href = `https://${hostname}`; document.head.appendChild(link); processed.add(hostname); preconnectCount++; if (config.debug) { console.log('[Preconnect] 已连接:', hostname); } }; const scanResources = () => { const resources = [ ...document.querySelectorAll('script[src], link[href], img[src]') ]; resources.forEach(el => { try { const url = new URL(el.src || el.href); const matched = whitelist.find(domain => url.hostname.endsWith(domain) ); if (matched) { doPreconnect(url.hostname); } } catch {} }); }; // MutationObserver回调防抖 const debouncedScan = debounce(scanResources, 50); scanResources(); setTimeout(scanResources, 2000); new MutationObserver(debouncedScan).observe(document.body, { childList: true, subtree: true }); // 统计日志 if (config.debug) { setTimeout(() => { console.log(`[预连接] 本页已预连接: ${preconnectCount}`); }, 2500); } } catch (e) { if (config.debug) console.warn('[Preconnect] 异常:', e); } }; // 资源预加载系统(修复字体预加载警告) const initResourcePreload = () => { if (optimizerSiteBlacklist.some(domain => location.hostname.includes(domain)) || preloadSiteBlacklist.some(domain => location.hostname.includes(domain))) { if (config.debug) console.log('[预加载] 当前站点已在全局或预加载黑名单,跳过预加载优化'); return; } if (!config.features.preload.enabled) return; try { const processed = new Set(); const types = config.features.preload.types; const max = config.features.preload.maxPreloads; const cssCache = new Map(); let preloadCount = 0; const shouldPreload = (url) => { const ext = url.split('.').pop().toLowerCase(); return types.includes(ext); }; const doPreload = (url, asType) => { if (processed.size >= max) return; if (processed.has(url)) return; const link = document.createElement('link'); link.rel = 'preload'; link.as = asType; link.href = url; if (asType === 'font') { link.setAttribute('crossorigin', 'anonymous'); 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'; } } else if (asType === 'script') { link.type = 'text/javascript'; } else if (asType === 'style') { link.type = 'text/css'; } document.head.appendChild(link); processed.add(url); preloadCount++; if (config.debug) { console.log('[Preload] 已预加载:', url); } }; const processFont = (cssUrl, fontUrl) => { try { const absoluteUrl = new URL(fontUrl, cssUrl).href; if (fontUrl.startsWith('data:')) return; if (shouldPreload(absoluteUrl)) { doPreload(absoluteUrl, 'font'); } } catch (e) { if (config.debug) { console.warn('[Preload] 字体解析失败:', fontUrl, e); } } }; const scanResources = () => { document.querySelectorAll('link[rel="stylesheet"]').forEach(link => { const cssUrl = link.href; if (cssUrl && shouldPreload(cssUrl)) { doPreload(cssUrl, 'style'); // fetch缓存 if (cssCache.has(cssUrl)) { const text = cssCache.get(cssUrl); const fontUrls = text.match(/url\(["']?([^")']+\.(woff2?|ttf|otf))["']?\)/gi) || []; fontUrls.forEach(fullUrl => { const cleanUrl = fullUrl.replace(/url\(["']?|["']?\)/g, ''); processFont(cssUrl, cleanUrl); }); } else { fetch(cssUrl) .then(res => res.text()) .then(text => { cssCache.set(cssUrl, text); const fontUrls = text.match(/url\(["']?([^")']+\.(woff2?|ttf|otf))["']?\)/gi) || []; fontUrls.forEach(fullUrl => { const cleanUrl = fullUrl.replace(/url\(["']?|["']?\)/g, ''); processFont(cssUrl, cleanUrl); }); }) .catch(e => { if (config.debug) { console.warn('[Preload] CSS获取失败:', cssUrl, e); } }); } } }); document.querySelectorAll('script[src]').forEach(script => { const src = script.src; if (src && shouldPreload(src)) { doPreload(src, 'script'); } }); }; // MutationObserver回调防抖 const debouncedScan = debounce(scanResources, 50); scanResources(); new MutationObserver(debouncedScan).observe(document.body, { childList: true, subtree: true }); // 定期清理cssCache和processed,防止无限增长 scheduleCleanup(() => { cssCache.clear(); processed.clear(); if (config.debug) console.log('[Preload] 缓存已清理'); }, 10 * 60 * 1000); // 10分钟 // 统计日志 if (config.debug) { setTimeout(() => { console.log(`[预加载] 本页已预加载: ${preloadCount}`); }, 2500); } } catch (e) { if (config.debug) console.warn('[Preload] 异常:', e); } }; // 布局稳定性优化 const initLayoutStabilization = () => { if (optimizerSiteBlacklist.some(domain => location.hostname.includes(domain)) || layoutSiteBlacklist.some(domain => location.hostname.includes(domain))) { if (config.debug) console.log('[布局优化] 当前站点已在全局或布局黑名单,跳过布局优化'); return; } try { const styles = []; if (config.features.layout.stableImages) { styles.push(` img:not([width]):not([height]) { min-height: 1px; // display: block; // max-width: 100%; } @supports (aspect-ratio: 1/1) { img:not([width]):not([height]) { aspect-ratio: attr(width) / attr(height); } } `); } if (config.features.layout.stableIframes) { styles.push(` iframe:not([width]):not([height]) { // width: 100%; height: auto; // aspect-ratio: 16/9; // display: block; } `); } if (styles.length) { const style = document.createElement('style'); style.textContent = styles.join('\n'); document.head.appendChild(style); if (config.debug) { console.log('[布局优化] 样式已注入'); } } } catch (e) { if (config.debug) console.warn('[Layout] 异常:', e); } }; // ======================== // 工具函数 // ======================== function throttle(func, limit) { let inThrottle; return function() { if (!inThrottle) { func.apply(this, arguments); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } // ======================== // 初始化系统 // ======================== document.addEventListener('DOMContentLoaded', () => { initSmartPreconnect(); initResourcePreload(); initLazyLoad(); initLayoutStabilization(); if (config.debug) { console.groupCollapsed('[Optimizer] 初始化报告'); console.log('激活功能:', Object.entries(config.features) .filter(([_, v]) => v.enabled !== false) .map(([k]) => k)); console.log('预连接白名单:', config.features.preconnect.whitelist); console.log('预加载类型:', config.features.preload.types); console.groupEnd(); } }); })();