// ==UserScript==
// @name 网页加速器专业版
// @namespace https://docs.scriptcat.org/
// @version 2.0.0
// @description 智能预加载和缓存网页,显著提升浏览速度
// @author HyperNav
// @match https://*/*
// @match http://*/*
// @grant none
// @noframes
// @icon https://img.icons8.com/color/96/000000/rocket.png
// ==/UserScript==
(function() {
'use strict';
// 确保只运行一次
if (window.hypernavLoaded) return;
window.hypernavLoaded = true;
// 创建样式
const style = document.createElement('style');
style.textContent = `
.hypernav-container {
all: initial;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 12px;
z-index: 2147483647;
}
.hypernav-toggle-btn {
position: fixed;
bottom: 20px;
right: 20px;
width: 48px;
height: 48px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 20px rgba(102, 126, 234, 0.5);
transition: all 0.3s ease;
z-index: 2147483646;
}
.hypernav-toggle-btn:hover {
transform: scale(1.1);
box-shadow: 0 6px 30px rgba(102, 126, 234, 0.7);
}
.hypernav-panel {
position: fixed;
bottom: 80px;
right: 20px;
width: 350px;
background: white;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
overflow: hidden;
z-index: 2147483645;
opacity: 0;
transform: translateY(20px);
transition: all 0.3s ease;
}
.hypernav-panel.show {
opacity: 1;
transform: translateY(0);
}
.hypernav-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 15px 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.hypernav-title {
display: flex;
align-items: center;
gap: 10px;
}
.hypernav-title h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
}
.hypernav-badge {
background: rgba(255, 255, 255, 0.2);
padding: 2px 8px;
border-radius: 10px;
font-size: 10px;
}
.hypernav-close-btn {
background: none;
border: none;
color: white;
cursor: pointer;
font-size: 20px;
line-height: 1;
padding: 5px;
border-radius: 4px;
}
.hypernav-close-btn:hover {
background: rgba(255, 255, 255, 0.2);
}
.hypernav-content {
padding: 20px;
max-height: 400px;
overflow-y: auto;
}
.hypernav-stats {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
margin-bottom: 20px;
}
.hypernav-stat {
background: #f5f5f5;
padding: 10px;
border-radius: 8px;
text-align: center;
}
.hypernav-stat-value {
font-size: 20px;
font-weight: 700;
color: #667eea;
margin-bottom: 4px;
}
.hypernav-stat-label {
font-size: 11px;
color: #666;
}
.hypernav-network-status {
display: flex;
align-items: center;
gap: 8px;
padding: 10px;
background: #f0f7ff;
border-radius: 8px;
margin-bottom: 20px;
}
.hypernav-network-indicator {
width: 8px;
height: 8px;
border-radius: 50%;
}
.hypernav-network-good { background: #4caf50; }
.hypernav-network-medium { background: #ff9800; }
.hypernav-network-poor { background: #f44336; }
.hypernav-controls {
margin-bottom: 20px;
}
.hypernav-control-group {
margin-bottom: 15px;
}
.hypernav-control-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.hypernav-switch {
position: relative;
display: inline-block;
width: 40px;
height: 20px;
}
.hypernav-switch input {
opacity: 0;
width: 0;
height: 0;
}
.hypernav-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 20px;
}
.hypernav-slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 2px;
bottom: 2px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
.hypernav-switch input:checked + .hypernav-slider {
background-color: #667eea;
}
.hypernav-switch input:checked + .hypernav-slider:before {
transform: translateX(20px);
}
.hypernav-select {
padding: 6px 10px;
border: 1px solid #ddd;
border-radius: 6px;
background: white;
font-size: 12px;
min-width: 120px;
}
.hypernav-slider-container {
display: flex;
align-items: center;
gap: 10px;
}
.hypernav-range {
width: 100px;
}
.hypernav-range-value {
min-width: 40px;
text-align: right;
}
.hypernav-buttons {
display: flex;
gap: 10px;
}
.hypernav-btn {
flex: 1;
padding: 10px;
border: none;
border-radius: 8px;
background: #f5f5f5;
color: #333;
cursor: pointer;
font-size: 12px;
transition: all 0.2s;
}
.hypernav-btn:hover {
background: #e5e5e5;
}
.hypernav-btn-primary {
background: #667eea;
color: white;
}
.hypernav-btn-primary:hover {
background: #5a6fd8;
}
.hypernav-footer {
padding: 15px 20px;
background: #f9f9f9;
border-top: 1px solid #eee;
text-align: center;
color: #666;
font-size: 11px;
}
.hypernav-drag-handle {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 30px;
cursor: move;
}
.hypernav-log {
font-size: 10px;
color: #999;
margin-top: 5px;
}
.hypernav-tab-container {
display: flex;
border-bottom: 1px solid #eee;
margin-bottom: 20px;
}
.hypernav-tab {
padding: 10px 20px;
cursor: pointer;
border-bottom: 2px solid transparent;
}
.hypernav-tab.active {
border-bottom-color: #667eea;
color: #667eea;
font-weight: 600;
}
.hypernav-tab-content {
display: none;
}
.hypernav-tab-content.active {
display: block;
}
.hypernav-log-entry {
padding: 8px;
border-bottom: 1px solid #eee;
font-family: 'Courier New', monospace;
font-size: 11px;
}
.hypernav-log-entry.success { color: #4caf50; }
.hypernav-log-entry.warning { color: #ff9800; }
.hypernav-log-entry.error { color: #f44336; }
.hypernav-cache-list {
max-height: 200px;
overflow-y: auto;
}
.hypernav-cache-item {
padding: 8px;
border-bottom: 1px solid #eee;
font-size: 11px;
display: flex;
justify-content: space-between;
}
.hypernav-cache-url {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 200px;
}
.hypernav-cache-size {
color: #666;
}
`;
document.head.appendChild(style);
// 主类
class HyperNav {
constructor() {
this.config = {
enabled: true,
mode: 'balanced',
prefetchMethod: 'viewport',
maxConcurrent: 3,
delay: 150,
threshold: 0.3,
timeout: 5000,
cacheDuration: 300000,
learnBehavior: true,
showUI: true,
debug: false
};
this.state = {
initialized: false,
active: false,
linksObserved: 0,
linksPrefetched: 0,
cacheHits: 0,
cache: new Map(),
observer: null,
uiVisible: false
};
this.ui = {
container: null,
panel: null,
updateInterval: null
};
this.ignorePatterns = [
/\.(jpg|jpeg|png|gif|webp|svg|ico|mp4|mp3|pdf|zip|rar)$/i,
/logout|signout|delete|remove|checkout|payment/i,
/\/api\//i,
/^javascript:/i,
/^mailto:/i,
/^tel:/i
];
this.init();
}
init() {
this.loadConfig();
this.createUI();
this.setupEventListeners();
if (this.config.enabled) {
this.start();
}
this.state.initialized = true;
this.log('HyperNav 初始化完成');
}
loadConfig() {
try {
const saved = localStorage.getItem('hypernav-config');
if (saved) {
Object.assign(this.config, JSON.parse(saved));
}
} catch (e) {
console.warn('加载配置失败:', e);
}
}
saveConfig() {
try {
localStorage.setItem('hypernav-config', JSON.stringify(this.config));
} catch (e) {
console.warn('保存配置失败:', e);
}
}
createUI() {
if (!this.config.showUI) return;
// 创建容器
this.ui.container = document.createElement('div');
this.ui.container.className = 'hypernav-container';
document.body.appendChild(this.ui.container);
// 创建切换按钮
const toggleBtn = document.createElement('button');
toggleBtn.className = 'hypernav-toggle-btn';
toggleBtn.innerHTML = '🚀';
toggleBtn.title = '网页加速器控制面板';
this.ui.container.appendChild(toggleBtn);
// 创建控制面板
this.ui.panel = document.createElement('div');
this.ui.panel.className = 'hypernav-panel';
this.ui.panel.innerHTML = `
`;
this.ui.container.appendChild(this.ui.panel);
this.setupUIEvents();
}
setupUIEvents() {
const panel = this.ui.panel;
// 切换面板显示
this.ui.container.querySelector('.hypernav-toggle-btn').addEventListener('click', () => {
panel.classList.toggle('show');
});
// 关闭面板
panel.querySelector('.hypernav-close-btn').addEventListener('click', () => {
panel.classList.remove('show');
});
// 标签切换
panel.querySelectorAll('.hypernav-tab').forEach(tab => {
tab.addEventListener('click', () => {
panel.querySelectorAll('.hypernav-tab').forEach(t => t.classList.remove('active'));
panel.querySelectorAll('.hypernav-tab-content').forEach(c => c.classList.remove('active'));
tab.classList.add('active');
const tabId = tab.dataset.tab;
panel.querySelector(`#tab-${tabId}`).classList.add('active');
});
});
// 控制项事件
panel.querySelector('#toggle-enabled').addEventListener('change', (e) => {
this.config.enabled = e.target.checked;
this.saveConfig();
if (this.config.enabled) {
this.start();
} else {
this.stop();
}
});
panel.querySelector('#select-mode').addEventListener('change', (e) => {
this.config.mode = e.target.value;
this.saveConfig();
});
panel.querySelector('#toggle-learning').addEventListener('change', (e) => {
this.config.learnBehavior = e.target.checked;
this.saveConfig();
});
panel.querySelector('#range-concurrent').addEventListener('input', (e) => {
const value = e.target.value;
panel.querySelector('#value-concurrent').textContent = value;
this.config.maxConcurrent = parseInt(value);
this.saveConfig();
});
panel.querySelector('#range-delay').addEventListener('input', (e) => {
const value = e.target.value;
panel.querySelector('#value-delay').textContent = `${value}ms`;
this.config.delay = parseInt(value);
this.saveConfig();
});
// 按钮事件
panel.querySelector('#btn-refresh').addEventListener('click', () => {
this.updateUI();
});
panel.querySelector('#btn-clear-cache').addEventListener('click', () => {
if (confirm('确定要清空所有缓存吗?')) {
this.state.cache.clear();
this.state.cacheHits = 0;
this.updateUI();
this.log('缓存已清空');
}
});
// 拖动功能
let isDragging = false;
let startX, startY, startLeft, startTop;
panel.querySelector('.hypernav-drag-handle').addEventListener('mousedown', (e) => {
isDragging = true;
startX = e.clientX;
startY = e.clientY;
const rect = panel.getBoundingClientRect();
startLeft = rect.left;
startTop = rect.top;
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
const onMouseMove = (e) => {
if (!isDragging) return;
const dx = e.clientX - startX;
const dy = e.clientY - startY;
panel.style.left = (startLeft + dx) + 'px';
panel.style.top = (startTop + dy) + 'px';
};
const onMouseUp = () => {
isDragging = false;
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
};
// 初始化控制项
panel.querySelector('#toggle-enabled').checked = this.config.enabled;
panel.querySelector('#select-mode').value = this.config.mode;
panel.querySelector('#toggle-learning').checked = this.config.learnBehavior;
panel.querySelector('#range-concurrent').value = this.config.maxConcurrent;
panel.querySelector('#value-concurrent').textContent = this.config.maxConcurrent;
panel.querySelector('#range-delay').value = this.config.delay;
panel.querySelector('#value-delay').textContent = `${this.config.delay}ms`;
}
setupEventListeners() {
// 键盘快捷键
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.shiftKey && e.key === 'H') {
e.preventDefault();
this.ui.panel.classList.toggle('show');
}
if (e.ctrlKey && e.shiftKey && e.key === ' ') {
e.preventDefault();
this.toggleEnabled();
}
});
// 监听链接点击
document.addEventListener('click', (e) => {
let target = e.target;
while (target && target.tagName !== 'A') {
target = target.parentElement;
}
if (target && target.href) {
this.log('链接点击', target.href);
}
}, true);
// 监听页面可见性变化
document.addEventListener('visibilitychange', () => {
if (!document.hidden && this.config.enabled) {
this.start();
}
});
}
toggleEnabled() {
this.config.enabled = !this.config.enabled;
this.saveConfig();
if (this.config.enabled) {
this.start();
} else {
this.stop();
}
this.updateUI();
this.log(`加速器已${this.config.enabled ? '启用' : '禁用'}`);
}
start() {
if (this.state.active) return;
this.observeLinks();
this.state.active = true;
// 启动UI更新
if (this.ui.updateInterval) {
clearInterval(this.ui.updateInterval);
}
this.ui.updateInterval = setInterval(() => {
this.updateUI();
}, 1000);
this.log('加速器已启动');
}
stop() {
if (this.state.observer) {
this.state.observer.disconnect();
}
this.state.active = false;
if (this.ui.updateInterval) {
clearInterval(this.ui.updateInterval);
this.ui.updateInterval = null;
}
this.log('加速器已停止');
}
observeLinks() {
if (!this.config.enabled) return;
if (this.state.observer) {
this.state.observer.disconnect();
}
this.state.observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const link = entry.target;
this.handleLink(link);
}
});
}, {
root: null,
rootMargin: '100px',
threshold: 0.1
});
const links = Array.from(document.querySelectorAll('a[href]')).filter(link => {
return !link.href.includes('#') &&
!link.href.includes('javascript:') &&
!this.shouldIgnore(link.href);
});
links.forEach(link => {
this.state.observer.observe(link);
this.state.linksObserved++;
// 鼠标悬停预取
link.addEventListener('mouseenter', () => {
if (this.config.prefetchMethod === 'hover') {
this.prefetchLink(link);
}
}, { once: true });
});
this.log(`开始观察 ${links.length} 个链接`);
}
shouldIgnore(url) {
try {
for (const pattern of this.ignorePatterns) {
if (pattern.test(url)) {
return true;
}
}
return false;
} catch (e) {
return true;
}
}
async prefetchLink(link) {
if (!link || !link.href || !this.config.enabled) return;
const url = link.href;
// 检查缓存
if (this.state.cache.has(url)) {
const cached = this.state.cache.get(url);
if (Date.now() - cached.timestamp < this.config.cacheDuration) {
this.state.cacheHits++;
return true;
}
}
// 并发控制
if (this.state.activePrefetches >= this.config.maxConcurrent) {
return false;
}
this.state.activePrefetches = (this.state.activePrefetches || 0) + 1;
try {
// 延迟预取
await new Promise(resolve => setTimeout(resolve, this.config.delay));
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
const response = await fetch(url, {
method: 'HEAD',
mode: 'cors',
credentials: 'same-origin',
signal: controller.signal,
headers: {
'X-Purpose': 'prefetch'
}
});
clearTimeout(timeoutId);
if (response.ok) {
const contentType = response.headers.get('content-type') || '';
if (contentType.includes('text/html')) {
this.state.cache.set(url, {
timestamp: Date.now(),
size: parseInt(response.headers.get('content-length') || '0')
});
this.state.linksPrefetched++;
this.log(`预取成功: ${new URL(url).pathname}`);
return true;
}
}
} catch (error) {
if (this.config.debug) {
console.log('预取失败:', url, error);
}
} finally {
this.state.activePrefetches--;
}
return false;
}
handleLink(link) {
if (link._hypernavProcessed) return;
link._hypernavProcessed = true;
this.prefetchLink(link);
// 学习用户行为
link.addEventListener('click', () => {
this.log(`用户点击: ${new URL(link.href).pathname}`);
}, { once: true });
}
updateUI() {
if (!this.ui.panel || !this.ui.panel.classList.contains('show')) return;
const panel = this.ui.panel;
// 更新统计
panel.querySelector('#stat-observed').textContent = this.state.linksObserved;
panel.querySelector('#stat-prefetched').textContent = this.state.linksPrefetched;
panel.querySelector('#stat-cache-hits').textContent = this.state.cacheHits;
// 计算加速效果
const speedImprovement = this.calculateSpeedImprovement();
panel.querySelector('#stat-speed').textContent = `${speedImprovement}%`;
panel.querySelector('#stat-speed').style.color =
speedImprovement > 50 ? '#4caf50' :
speedImprovement > 20 ? '#ff9800' : '#f44336';
// 更新网络状态
this.updateNetworkStatus();
// 更新缓存列表
this.updateCacheList();
// 更新页脚
panel.querySelector('#footer-stats').textContent =
`缓存: ${this.state.cache.size} | 最后更新: ${new Date().toLocaleTimeString()}`;
}
calculateSpeedImprovement() {
if (this.state.linksObserved === 0) return 0;
const hitRate = (this.state.cacheHits / this.state.linksObserved) * 100;
return Math.min(99, Math.round(hitRate * 1.5));
}
updateNetworkStatus() {
const element = this.ui.panel.querySelector('#network-status');
if (!element) return;
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
if (connection) {
const type = connection.effectiveType || 'unknown';
const downlink = connection.downlink || 0;
const rtt = connection.rtt || 0;
let status = '良好';
let indicatorClass = 'hypernav-network-good';
if (type.includes('2g') || rtt > 300) {
status = '较慢';
indicatorClass = 'hypernav-network-medium';
}
if (type.includes('slow-2g') || rtt > 1000) {
status = '极慢';
indicatorClass = 'hypernav-network-poor';
}
element.innerHTML = `
网络: ${type.toUpperCase()} | 延迟: ${rtt}ms | 速度: ${downlink}Mbps
`;
} else {
element.innerHTML = `
网络状态: 未知
`;
}
}
updateCacheList() {
const container = this.ui.panel.querySelector('#cache-list');
if (!container) return;
if (this.state.cache.size === 0) {
container.innerHTML = '缓存为空
';
return;
}
let html = '';
let count = 0;
for (const [url, data] of this.state.cache.entries()) {
if (count++ >= 20) break; // 只显示前20条
const age = Date.now() - data.timestamp;
const ageText = age < 60000 ? '刚刚' :
age < 3600000 ? `${Math.floor(age / 60000)}分钟前` :
`${Math.floor(age / 3600000)}小时前`;
const urlObj = new URL(url);
const displayUrl = urlObj.pathname + urlObj.search;
html += `
`;
}
container.innerHTML = html;
}
log(message, type = 'info') {
if (!this.config.debug && type === 'info') return;
const logContainer = this.ui.panel?.querySelector('#log-container');
if (!logContainer) return;
const entry = document.createElement('div');
entry.className = `hypernav-log-entry ${type}`;
entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
logContainer.appendChild(entry);
// 限制日志数量
const entries = logContainer.querySelectorAll('.hypernav-log-entry');
if (entries.length > 50) {
entries[0].remove();
}
// 自动滚动到底部
logContainer.scrollTop = logContainer.scrollHeight;
}
getStats() {
return {
enabled: this.config.enabled,
mode: this.config.mode,
linksObserved: this.state.linksObserved,
linksPrefetched: this.state.linksPrefetched,
cacheHits: this.state.cacheHits,
cacheSize: this.state.cache.size,
speedImprovement: this.calculateSpeedImprovement() + '%'
};
}
}
// 初始化
window.hypernav = new HyperNav();
// 在控制台暴露API
console.log('🚀 网页加速器已加载!使用 Ctrl+Shift+H 打开控制面板');
console.log('可用命令: hypernav.getStats()');
})();