思源笔记性能优化套件 v1.1 (带悬浮球监控版)

用 ai 跑了一个性能优化 js,不知道有没有用,大家可以试试。

【AI 生成代码 风险自担】

  1. 使用者须自行测试验证
  2. 因使用造成的损失自行承担
// ================================================================
// 思源笔记性能优化套件 v1.1
// 版本:1.1 (悬浮球监控版)
// 功能:搜索优化、内存管理、渲染优化、批量保存、悬浮监控球
// 安装:设置 → 外观 → 代码片段 → 粘贴此代码 → 重启思源
// ================================================================

(function() {
    'use strict';
    
    console.log('[思源性能优化] 正在加载...');
    
    // ==================== 配置参数 ====================
    const CONFIG = {
        // 防抖延迟(毫秒)
        SEARCH_DEBOUNCE: 300,
        INPUT_DEBOUNCE: 150,
        
        // 内存清理间隔(分钟)
        MEMORY_CLEANUP_MINUTES: 30,
        
        // 批量保存设置
        SAVE_BATCH_SIZE: 10,
        SAVE_BATCH_DELAY: 300,
        
        // 缓存设置
        MAX_CACHE_SIZE: 100,
        CACHE_EXPIRE_MINUTES: 10,
        
        // 监控球设置
        MONITOR_BALL_SIZE: 32,          // 悬浮球大小(像素)
        MONITOR_BALL_OPACITY: 0.6,      // 悬浮球默认透明度
        MONITOR_BALL_HOVER_OPACITY: 0.9, // 悬停时透明度
        MONITOR_BALL_POSITION: 'right', // 默认位置: left/right
        MONITOR_UPDATE_INTERVAL: 2000,   // 监控更新频率(毫秒)
        
        // 面板设置
        PANEL_OPACITY: 0.95,            // 面板透明度
        PANEL_HOVER_OPACITY: 1.0        // 面板悬停透明度
    };
    
    // ==================== 性能统计 ====================
    class PerformanceStats {
        constructor() {
            this.metrics = {
                fps: 0,
                memory: { used: 0, total: 0 },
                domCount: 0,
                saveCount: 0,
                searchCount: 0,
                cacheHits: 0,
                cacheMisses: 0
            };
            this.startTime = Date.now();
            this.frameCount = 0;
            this.lastFpsTime = performance.now();
            this.initFPSMonitor();
            this.initMemoryMonitor();
        }
        
        initFPSMonitor() {
            const updateFPS = () => {
                this.frameCount++;
                const now = performance.now();
                if (now - this.lastFpsTime >= 1000) {
                    this.metrics.fps = Math.min(this.frameCount, 60);
                    this.frameCount = 0;
                    this.lastFpsTime = now;
                }
                requestAnimationFrame(updateFPS);
            };
            requestAnimationFrame(updateFPS);
        }
        
        initMemoryMonitor() {
            setInterval(() => {
                if (performance.memory) {
                    this.metrics.memory.used = Math.round(performance.memory.usedJSHeapSize / 1048576);
                    this.metrics.memory.total = Math.round(performance.memory.totalJSHeapSize / 1048576);
                }
                this.metrics.domCount = document.getElementsByTagName('*').length;
            }, 3000);
        }
        
        recordSave(duration) {
            this.metrics.saveCount++;
        }
        
        recordSearch(duration, cacheHit = false) {
            this.metrics.searchCount++;
            cacheHit ? this.metrics.cacheHits++ : this.metrics.cacheMisses++;
        }
        
        getCacheRate() {
            const total = this.metrics.cacheHits + this.metrics.cacheMisses;
            return total > 0 ? Math.round(this.metrics.cacheHits / total * 100) : 0;
        }
        
        getUptime() {
            const seconds = Math.floor((Date.now() - this.startTime) / 1000);
            const h = Math.floor(seconds / 3600);
            const m = Math.floor((seconds % 3600) / 60);
            return h > 0 ? `${h}h ${m}m` : `${m}m`;
        }
        
        getFPSColor(fps) {
            if (fps >= 55) return '#4CAF50';
            if (fps >= 30) return '#FFA726';
            return '#F44336';
        }
        
        getMemoryColor(mem) {
            const usedMB = parseInt(mem);
            if (usedMB < 1000) return '#4CAF50';
            if (usedMB < 2000) return '#FFA726';
            return '#F44336';
        }
        
        getCacheColor(rate) {
            if (rate >= 60) return '#4CAF50';
            if (rate >= 30) return '#FFA726';
            return '#F44336';
        }
        
        getSummary() {
            return {
                fps: this.metrics.fps,
                fpsColor: this.getFPSColor(this.metrics.fps),
                memory: `${this.metrics.memory.used}MB`,
                memoryColor: this.getMemoryColor(this.metrics.memory.used),
                dom: this.metrics.domCount,
                cacheRate: this.getCacheRate(),
                cacheColor: this.getCacheColor(this.getCacheRate()),
                uptime: this.getUptime(),
                saves: this.metrics.saveCount,
                searches: this.metrics.searchCount
            };
        }
    }
    
    // ==================== 悬浮监控球 ====================
    class MonitorBall {
        constructor(stats) {
            this.stats = stats;
            this.ball = null;
            this.panel = null;
            this.isPanelVisible = false;
            this.isDragging = false;
            this.dragOffset = { x: 0, y: 0 };
            this.init();
        }
        
        init() {
            this.createBall();
            this.createPanel();
            this.setupEventListeners();
            this.updateLoop();
            console.log('[监控球] 悬浮监控球已启用');
        }
        
        createBall() {
            this.ball = document.createElement('div');
            this.ball.id = 'siyuan-monitor-ball';
            this.ball.style.cssText = `
                position: fixed;
                ${CONFIG.MONITOR_BALL_POSITION === 'right' ? 'right: 20px' : 'left: 20px'};
                top: 50%;
                transform: translateY(-50%);
                width: ${CONFIG.MONITOR_BALL_SIZE}px;
                height: ${CONFIG.MONITOR_BALL_SIZE}px;
                border-radius: 50%;
                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                color: white;
                display: flex;
                align-items: center;
                justify-content: center;
                font-size: 12px;
                font-weight: bold;
                cursor: pointer;
                z-index: 99998;
                opacity: ${CONFIG.MONITOR_BALL_OPACITY};
                transition: all 0.3s ease;
                box-shadow: 0 4px 12px rgba(0,0,0,0.2);
                user-select: none;
            `;
            
            document.body.appendChild(this.ball);
        }
        
        createPanel() {
            this.panel = document.createElement('div');
            this.panel.id = 'siyuan-monitor-panel';
            this.panel.style.cssText = `
                position: fixed;
                ${CONFIG.MONITOR_BALL_POSITION === 'right' ? 'right: 60px' : 'left: 60px'};
                top: 50%;
                transform: translateY(-50%);
                background: rgba(20, 20, 20, ${CONFIG.PANEL_OPACITY});
                color: #e0e0e0;
                padding: 15px;
                border-radius: 10px;
                font-family: 'Consolas', 'Monaco', monospace;
                font-size: 12px;
                z-index: 99997;
                border: 1px solid #333;
                box-shadow: 0 8px 24px rgba(0,0,0,0.3);
                backdrop-filter: blur(10px);
                min-width: 220px;
                user-select: none;
                opacity: 0;
                pointer-events: none;
                transition: opacity 0.3s ease, transform 0.3s ease;
            `;
            
            document.body.appendChild(this.panel);
        }
        
        setupEventListeners() {
            // 悬浮球点击切换面板
            this.ball.addEventListener('click', (e) => {
                e.stopPropagation();
                this.togglePanel();
            });
            
            // 悬浮球悬停效果
            this.ball.addEventListener('mouseenter', () => {
                this.ball.style.opacity = CONFIG.MONITOR_BALL_HOVER_OPACITY;
                this.ball.style.transform = 'translateY(-50%) scale(1.1)';
            });
            
            this.ball.addEventListener('mouseleave', () => {
                this.ball.style.opacity = CONFIG.MONITOR_BALL_OPACITY;
                this.ball.style.transform = 'translateY(-50%) scale(1)';
            });
            
            // 面板悬停效果
            this.panel.addEventListener('mouseenter', () => {
                this.panel.style.opacity = CONFIG.PANEL_HOVER_OPACITY;
            });
            
            this.panel.addEventListener('mouseleave', () => {
                if (this.isPanelVisible) {
                    this.panel.style.opacity = CONFIG.PANEL_OPACITY;
                }
            });
            
            // 悬浮球拖动功能
            this.ball.addEventListener('mousedown', (e) => {
                this.startDrag(e);
                e.preventDefault();
            });
            
            document.addEventListener('mousemove', (e) => {
                this.drag(e);
            });
            
            document.addEventListener('mouseup', () => {
                this.stopDrag();
            });
            
            // 点击页面其他地方关闭面板
            document.addEventListener('click', (e) => {
                if (this.isPanelVisible && 
                    !this.ball.contains(e.target) && 
                    !this.panel.contains(e.target)) {
                    this.hidePanel();
                }
            });
        }
        
        startDrag(e) {
            this.isDragging = true;
            const rect = this.ball.getBoundingClientRect();
            this.dragOffset.x = e.clientX - rect.left;
            this.dragOffset.y = e.clientY - rect.top;
            
            this.ball.style.cursor = 'grabbing';
            this.ball.style.transition = 'none';
        }
        
        drag(e) {
            if (!this.isDragging) return;
            
            const x = e.clientX - this.dragOffset.x;
            const y = e.clientY - this.dragOffset.y;
            
            // 限制在窗口范围内
            const maxX = window.innerWidth - this.ball.offsetWidth;
            const maxY = window.innerHeight - this.ball.offsetHeight;
            
            const clampedX = Math.max(0, Math.min(x, maxX));
            const clampedY = Math.max(0, Math.min(y, maxY));
            
            this.ball.style.left = clampedX + 'px';
            this.ball.style.top = clampedY + 'px';
            this.ball.style.right = 'auto';
            this.ball.style.transform = 'none';
            
            // 更新面板位置
            this.updatePanelPosition();
        }
        
        stopDrag() {
            if (!this.isDragging) return;
            
            this.isDragging = false;
            this.ball.style.cursor = 'pointer';
            this.ball.style.transition = 'all 0.3s ease';
            
            // 保存位置到本地存储
            this.savePosition();
        }
        
        updatePanelPosition() {
            if (!this.panel || !this.isPanelVisible) return;
            
            const ballRect = this.ball.getBoundingClientRect();
            const panelWidth = this.panel.offsetWidth;
            const windowWidth = window.innerWidth;
            
            // 判断面板显示在左边还是右边
            if (ballRect.left > windowWidth / 2) {
                // 球在右边,面板显示在左边
                this.panel.style.left = (ballRect.left - panelWidth - 10) + 'px';
                this.panel.style.right = 'auto';
            } else {
                // 球在左边,面板显示在右边
                this.panel.style.left = (ballRect.right + 10) + 'px';
                this.panel.style.right = 'auto';
            }
            
            this.panel.style.top = (ballRect.top + ballRect.height / 2 - this.panel.offsetHeight / 2) + 'px';
        }
        
        savePosition() {
            const ballRect = this.ball.getBoundingClientRect();
            localStorage.setItem('siyuan-monitor-ball-pos', JSON.stringify({
                x: ballRect.left,
                y: ballRect.top,
                timestamp: Date.now()
            }));
        }
        
        loadPosition() {
            try {
                const saved = localStorage.getItem('siyuan-monitor-ball-pos');
                if (saved) {
                    const pos = JSON.parse(saved);
                    if (Date.now() - pos.timestamp < 7 * 24 * 60 * 60 * 1000) { // 一周内
                        this.ball.style.left = pos.x + 'px';
                        this.ball.style.top = pos.y + 'px';
                        this.ball.style.right = 'auto';
                        this.ball.style.transform = 'none';
                        return true;
                    }
                }
            } catch (e) {
                // 忽略错误
            }
            return false;
        }
        
        togglePanel() {
            if (this.isPanelVisible) {
                this.hidePanel();
            } else {
                this.showPanel();
            }
        }
        
        showPanel() {
            this.isPanelVisible = true;
            this.updatePanel();
            this.updatePanelPosition();
            
            setTimeout(() => {
                this.panel.style.opacity = CONFIG.PANEL_OPACITY;
                this.panel.style.pointerEvents = 'auto';
                this.panel.style.transform = 'translateY(-50%) scale(1)';
            }, 10);
        }
        
        hidePanel() {
            this.isPanelVisible = false;
            this.panel.style.opacity = '0';
            this.panel.style.pointerEvents = 'none';
            this.panel.style.transform = 'translateY(-50%) scale(0.95)';
        }
        
        updateBall() {
            const summary = this.stats.getSummary();
            const fps = summary.fps;
            
            // 悬浮球显示FPS
            this.ball.textContent = fps;
            this.ball.style.background = summary.fpsColor;
            
            // 添加FPS状态提示
            let status = '优秀';
            if (fps < 30) status = '较差';
            else if (fps < 55) status = '良好';
            
            this.ball.title = `FPS: ${fps} (${status})\n点击查看详细性能信息`;
        }
        
        updatePanel() {
            if (!this.panel || !this.isPanelVisible) return;
            
            const summary = this.stats.getSummary();
            
            this.panel.innerHTML = `
                <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; border-bottom: 1px solid #444; padding-bottom: 8px;">
                    <strong style="color: #64B5F6; font-size: 13px;">⚡ 性能监控</strong>
                    <div style="display: flex; gap: 6px; align-items: center;">
                        <span style="font-size: 10px; color: #888; cursor: help;" title="可拖动悬浮球调整位置">📍</span>
                        <button onclick="window.SIYUAN_OPTIMIZER?.toggleMonitor()" style="background: none; border: none; color: #888; cursor: pointer; font-size: 14px; padding: 0; line-height: 1;" title="隐藏">−</button>
                    </div>
                </div>
                
                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 12px;">
                    <div style="background: rgba(255,255,255,0.05); padding: 6px; border-radius: 4px; border-left: 3px solid ${summary.fpsColor};">
                        <div style="font-size: 10px; color: #aaa;">FPS</div>
                        <div style="color: ${summary.fpsColor}; font-weight: bold; font-size: 14px;">${summary.fps}</div>
                    </div>
                    <div style="background: rgba(255,255,255,0.05); padding: 6px; border-radius: 4px; border-left: 3px solid ${summary.memoryColor};">
                        <div style="font-size: 10px; color: #aaa;">内存</div>
                        <div style="color: ${summary.memoryColor}; font-weight: bold; font-size: 14px;">${summary.memory}</div>
                    </div>
                    <div style="background: rgba(255,255,255,0.05); padding: 6px; border-radius: 4px; border-left: 3px solid #64B5F6;">
                        <div style="font-size: 10px; color: #aaa;">DOM节点</div>
                        <div style="color: #64B5F6; font-weight: bold; font-size: 14px;">${summary.dom}</div>
                    </div>
                    <div style="background: rgba(255,255,255,0.05); padding: 6px; border-radius: 4px; border-left: 3px solid ${summary.cacheColor};">
                        <div style="font-size: 10px; color: #aaa;">缓存命中</div>
                        <div style="color: ${summary.cacheColor}; font-weight: bold; font-size: 14px;">${summary.cacheRate}%</div>
                    </div>
                </div>
                
                <div style="font-size: 10px; color: #888; margin-bottom: 10px;">
                    <div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
                        <span>运行时间:</span>
                        <span>${summary.uptime}</span>
                    </div>
                    <div style="display: flex; justify-content: space-between;">
                        <span>操作统计:</span>
                        <span>保存${summary.saves}次 | 搜索${summary.searches}次</span>
                    </div>
                </div>
                
                <div style="display: flex; gap: 8px; border-top: 1px solid #333; padding-top: 10px;">
                    <button onclick="window.SIYUAN_OPTIMIZER?.cleanup()" style="flex: 1; background: #2196F3; color: white; border: none; padding: 6px; border-radius: 4px; cursor: pointer; font-size: 10px; transition: all 0.2s ease;">清理内存</button>
                    <button onclick="window.SIYUAN_OPTIMIZER?.clearCache()" style="flex: 1; background: #FF9800; color: white; border: none; padding: 6px; border-radius: 4px; cursor: pointer; font-size: 10px; transition: all 0.2s ease;">清空缓存</button>
                </div>
                
                <div style="margin-top: 8px; display: flex; gap: 8px;">
                    <button onclick="window.SIYUAN_OPTIMIZER?.flushSaves()" style="flex: 1; background: #4CAF50; color: white; border: none; padding: 6px; border-radius: 4px; cursor: pointer; font-size: 10px; transition: all 0.2s ease;">立即保存</button>
                    <button onclick="location.reload()" style="flex: 1; background: #9C27B0; color: white; border: none; padding: 6px; border-radius: 4px; cursor: pointer; font-size: 10px; transition: all 0.2s ease;">刷新应用</button>
                </div>
                
                <div style="margin-top: 8px; font-size: 9px; color: #666; text-align: center; padding-top: 8px; border-top: 1px solid #333;">
                    思源笔记性能优化套件 v1.1
                </div>
            `;
            
            // 添加按钮悬停效果
            this.panel.querySelectorAll('button').forEach(btn => {
                btn.addEventListener('mouseenter', () => {
                    btn.style.transform = 'translateY(-1px)';
                    btn.style.boxShadow = '0 2px 8px rgba(0,0,0,0.2)';
                });
                btn.addEventListener('mouseleave', () => {
                    btn.style.transform = 'translateY(0)';
                    btn.style.boxShadow = 'none';
                });
            });
        }
        
        updateLoop() {
            this.updateBall();
            if (this.isPanelVisible) {
                this.updatePanel();
            }
            setTimeout(() => this.updateLoop(), CONFIG.MONITOR_UPDATE_INTERVAL);
        }
        
        hide() {
            this.ball.style.display = 'none';
            this.hidePanel();
        }
        
        show() {
            this.ball.style.display = 'flex';
        }
        
        toggle() {
            if (this.ball.style.display === 'none') {
                this.show();
            } else {
                this.hide();
            }
        }
    }
    
    // ==================== 搜索优化器 ====================
    class SearchOptimizer {
        constructor(stats) {
            this.stats = stats;
            this.cache = new Map();
            this.pendingSearches = new Map();
            this.init();
        }
        
        init() {
            this.wrapSearchFunction();
            this.setupCacheCleaner();
        }
        
        wrapSearchFunction() {
            const originalSearch = window.siyuan?.search;
            if (!originalSearch) {
                setTimeout(() => this.wrapSearchFunction(), 1000);
                return;
            }
            
            window.siyuan.search = async (query, options = {}) => {
                if (!query || query.trim().length === 0) {
                    return [];
                }
                
                const cacheKey = this.getCacheKey(query, options);
                
                // 检查缓存
                const cached = this.cache.get(cacheKey);
                if (cached && Date.now() - cached.timestamp < CONFIG.CACHE_EXPIRE_MINUTES * 60000) {
                    this.stats.recordSearch(0, true);
                    return cached.results;
                }
                
                // 检查是否已有相同搜索在进行
                if (this.pendingSearches.has(cacheKey)) {
                    return this.pendingSearches.get(cacheKey);
                }
                
                // 创建防抖搜索
                const searchPromise = new Promise(resolve => {
                    setTimeout(async () => {
                        try {
                            const startTime = performance.now();
                            const results = await originalSearch(query, options);
                            const duration = performance.now() - startTime;
                            
                            // 缓存结果
                            if (query.length > 2) {
                                this.cache.set(cacheKey, {
                                    results,
                                    timestamp: Date.now(),
                                    query,
                                    duration
                                });
                                this.manageCacheSize();
                            }
                            
                            this.stats.recordSearch(duration, false);
                            resolve(results);
                        } catch (error) {
                            console.error('[搜索优化] 搜索失败:', error);
                            resolve([]);
                        } finally {
                            this.pendingSearches.delete(cacheKey);
                        }
                    }, CONFIG.SEARCH_DEBOUNCE);
                });
                
                this.pendingSearches.set(cacheKey, searchPromise);
                return searchPromise;
            };
            
            console.log('[搜索优化] 搜索函数已优化');
        }
        
        getCacheKey(query, options) {
            return JSON.stringify({
                q: query.trim().toLowerCase(),
                t: options.type || 'all'
            });
        }
        
        manageCacheSize() {
            if (this.cache.size > CONFIG.MAX_CACHE_SIZE) {
                let oldestKey = null;
                let oldestTime = Date.now();
                
                for (const [key, value] of this.cache.entries()) {
                    if (value.timestamp < oldestTime) {
                        oldestTime = value.timestamp;
                        oldestKey = key;
                    }
                }
                
                if (oldestKey) {
                    this.cache.delete(oldestKey);
                }
            }
        }
        
        setupCacheCleaner() {
            setInterval(() => {
                const now = Date.now();
                for (const [key, value] of this.cache.entries()) {
                    if (now - value.timestamp > CONFIG.CACHE_EXPIRE_MINUTES * 60000) {
                        this.cache.delete(key);
                    }
                }
            }, 60000);
        }
        
        clearCache() {
            this.cache.clear();
            console.log('[搜索优化] 缓存已清空');
        }
    }
    
    // ==================== 内存优化器 ====================
    class MemoryOptimizer {
        constructor() {
            this.init();
        }
        
        init() {
            this.setupLazyLoading();
            this.setupMemoryCleanup();
            console.log('[内存优化] 已启用');
        }
        
        setupLazyLoading() {
            const observer = new IntersectionObserver((entries) => {
                entries.forEach(entry => {
                    if (entry.isIntersecting) {
                        const img = entry.target;
                        if (img.dataset.src) {
                            img.src = img.dataset.src;
                            img.removeAttribute('data-src');
                        }
                        observer.unobserve(img);
                    }
                });
            }, {
                rootMargin: '300px',
                threshold: 0.01
            });
            
            setTimeout(() => {
                document.querySelectorAll('img[data-src]').forEach(img => {
                    observer.observe(img);
                });
            }, 1000);
            
            new MutationObserver(() => {
                document.querySelectorAll('img[data-src]').forEach(img => {
                    if (!img.hasAttribute('data-lazy-loaded')) {
                        observer.observe(img);
                        img.setAttribute('data-lazy-loaded', 'true');
                    }
                });
            }).observe(document.body, {
                childList: true,
                subtree: true
            });
        }
        
        setupMemoryCleanup() {
            setInterval(() => {
                this.cleanupHiddenElements();
                this.triggerGarbageCollection();
            }, CONFIG.MEMORY_CLEANUP_MINUTES * 60000);
            
            document.addEventListener('visibilitychange', () => {
                if (document.hidden) {
                    this.triggerGarbageCollection();
                }
            });
        }
        
        cleanupHiddenElements() {
            const hiddenElements = document.querySelectorAll('.fn__none, [style*="display: none"]');
            const now = Date.now();
            
            hiddenElements.forEach(el => {
                if (!el.hasAttribute('data-keep-in-memory')) {
                    const lastActive = parseInt(el.dataset.lastActive || '0');
                    if (now - lastActive > 300000) {
                        el.innerHTML = '';
                        el.dataset.cleaned = now;
                    }
                }
            });
        }
        
        triggerGarbageCollection() {
            if (window.gc) {
                try {
                    window.gc();
                    console.log('[内存优化] 垃圾回收已执行');
                } catch (e) {}
            }
        }
    }
    
    // ==================== 保存优化器 ====================
    class SaveOptimizer {
        constructor(stats) {
            this.stats = stats;
            this.saveQueue = [];
            this.saveTimer = null;
            this.init();
        }
        
        init() {
            this.wrapSaveFunction();
            this.setupAutoFlush();
            console.log('[保存优化] 批量保存已启用');
        }
        
        wrapSaveFunction() {
            const originalSave = window.siyuan?.saveTransaction;
            if (!originalSave) {
                setTimeout(() => this.wrapSaveFunction(), 1000);
                return;
            }
            
            window.siyuan.saveTransaction = (data) => {
                return new Promise((resolve) => {
                    this.saveQueue.push({ data, resolve });
                    
                    if (this.saveQueue.length >= CONFIG.SAVE_BATCH_SIZE) {
                        this.flushSaves(originalSave);
                    }
                    else if (!this.saveTimer) {
                        this.saveTimer = setTimeout(() => {
                            this.flushSaves(originalSave);
                        }, CONFIG.SAVE_BATCH_DELAY);
                    }
                });
            };
        }
        
        async flushSaves(originalSave) {
            if (this.saveTimer) {
                clearTimeout(this.saveTimer);
                this.saveTimer = null;
            }
            
            if (this.saveQueue.length === 0) return;
            
            const batch = [...this.saveQueue];
            this.saveQueue = [];
            
            const mergedData = this.mergeSaveData(batch);
            
            try {
                const startTime = performance.now();
                const result = await originalSave(mergedData);
                const duration = performance.now() - startTime;
                
                batch.forEach(item => item.resolve(result));
                
                this.stats.recordSave(duration);
                console.log(`[保存优化] 批量保存 ${batch.length} 个操作`);
                
            } catch (error) {
                console.error('[保存优化] 批量保存失败:', error);
                batch.forEach(item => item.resolve(null));
            }
        }
        
        mergeSaveData(batch) {
            return {
                operations: batch.flatMap(item => item.data.operations || []),
                blocks: batch.flatMap(item => item.data.blocks || []),
                batchSize: batch.length,
                timestamp: Date.now()
            };
        }
        
        setupAutoFlush() {
            setInterval(() => {
                if (this.saveQueue.length > 0) {
                    const originalSave = window.siyuan?.saveTransaction;
                    if (originalSave) {
                        this.flushSaves(originalSave);
                    }
                }
            }, 5 * 60000);
        }
        
        forceFlush() {
            const originalSave = window.siyuan?.saveTransaction;
            if (originalSave) {
                this.flushSaves(originalSave);
            }
        }
    }
    
    // ==================== 渲染优化器 ====================
    class RenderOptimizer {
        constructor() {
            this.init();
        }
        
        init() {
            this.injectOptimizedStyles();
            this.optimizeScrollPerformance();
            console.log('[渲染优化] CSS优化已应用');
        }
        
        injectOptimizedStyles() {
            const style = document.createElement('style');
            style.textContent = `
                .protyle-wysiwyg [data-node-id] {
                    contain: layout;
                }
                
                .protyle-content {
                    transform: translateZ(0);
                }
                
                img[data-src] {
                    opacity: 0;
                    transition: opacity 0.3s ease;
                }
                
                img[src]:not([data-src]) {
                    opacity: 1;
                }
                
                .fn__none {
                    display: none !important;
                }
            `;
            document.head.appendChild(style);
        }
        
        optimizeScrollPerformance() {
            let isScrolling = false;
            let scrollTimer = null;
            
            const handleScroll = () => {
                isScrolling = true;
                
                document.querySelectorAll('.protyle-attr, .protyle-toolbar').forEach(el => {
                    el.style.opacity = '0.7';
                });
                
                if (scrollTimer) clearTimeout(scrollTimer);
                scrollTimer = setTimeout(() => {
                    isScrolling = false;
                    document.querySelectorAll('.protyle-attr, .protyle-toolbar').forEach(el => {
                        el.style.opacity = '';
                    });
                }, 100);
            };
            
            window.addEventListener('scroll', handleScroll, { passive: true });
            window.addEventListener('wheel', handleScroll, { passive: true });
        }
    }
    
    // ==================== 主控制器 ====================
    class SiyuanOptimizer {
        constructor() {
            this.version = '1.1';
            this.modules = {};
            this.init();
        }
        
        init() {
            console.log(`%c思源笔记性能优化套件 v${this.version}`, 
                'background: linear-gradient(135deg, #667eea, #764ba2); color: white; padding: 6px 10px; border-radius: 4px;');
            
            const checkSiyuanLoaded = () => {
                if (window.siyuan) {
                    this.startOptimization();
                } else {
                    setTimeout(checkSiyuanLoaded, 500);
                }
            };
            
            checkSiyuanLoaded();
        }
        
        startOptimization() {
            try {
                this.modules.stats = new PerformanceStats();
                this.modules.memory = new MemoryOptimizer();
                this.modules.render = new RenderOptimizer();
                this.modules.search = new SearchOptimizer(this.modules.stats);
                this.modules.save = new SaveOptimizer(this.modules.stats);
                this.modules.monitor = new MonitorBall(this.modules.stats);
                
                this.setupGlobalAPI();
                
                setTimeout(() => {
                    console.log('✅ 思源笔记性能优化已启用!');
                    console.log('🎯 功能: 悬浮监控球、搜索加速、内存优化、批量保存');
                    console.log('⚡ 使用: SIYUAN_OPTIMIZER.help() 查看可用命令');
                }, 1000);
                
            } catch (error) {
                console.error('[思源优化] 初始化失败:', error);
            }
        }
        
        setupGlobalAPI() {
            window.SIYUAN_OPTIMIZER = {
                version: this.version,
                
                cleanup: () => {
                    if (window.gc) {
                        try {
                            window.gc();
                            console.log('[优化器] 内存清理完成');
                        } catch (e) {}
                    }
                },
                
                flushSaves: () => {
                    if (this.modules.save) {
                        this.modules.save.forceFlush();
                    }
                },
                
                clearCache: () => {
                    if (this.modules.search) {
                        this.modules.search.clearCache();
                    }
                },
                
                toggleMonitor: () => {
                    if (this.modules.monitor) {
                        this.modules.monitor.toggle();
                    }
                },
                
                showMonitor: () => {
                    if (this.modules.monitor) {
                        this.modules.monitor.show();
                    }
                },
                
                hideMonitor: () => {
                    if (this.modules.monitor) {
                        this.modules.monitor.hide();
                    }
                },
                
                getStats: () => {
                    return this.modules.stats ? this.modules.stats.getSummary() : null;
                },
                
                getConfig: () => {
                    return CONFIG;
                },
                
                help: () => {
                    console.log(`
思源笔记性能优化套件 v${this.version} - 可用命令:

SIYUAN_OPTIMIZER.help()         显示帮助信息
SIYUAN_OPTIMIZER.getStats()     获取性能统计数据
SIYUAN_OPTIMIZER.cleanup()      手动清理内存
SIYUAN_OPTIMIZER.flushSaves()   立即执行所有保存
SIYUAN_OPTIMIZER.clearCache()   清空搜索缓存
SIYUAN_OPTIMIZER.toggleMonitor()显示/隐藏悬浮监控球
SIYUAN_OPTIMIZER.showMonitor()  显示悬浮监控球
SIYUAN_OPTIMIZER.hideMonitor()  隐藏悬浮监控球
                    `);
                }
            };
        }
    }
    
    // ==================== 启动优化器 ====================
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
            setTimeout(() => {
                new SiyuanOptimizer();
            }, 2000);
        });
    } else {
        setTimeout(() => {
            new SiyuanOptimizer();
        }, 2000);
    }
    
    window.addEventListener('beforeunload', () => {
        const optimizer = window.SIYUAN_OPTIMIZER;
        if (optimizer && optimizer.flushSaves) {
            optimizer.flushSaves();
        }
        
        localStorage.setItem('siyuan-optimizer-last-run', Date.now());
    });
    
})();
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    28446 引用 • 119769 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 单点登录

    单点登录(Single Sign On)是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

    9 引用 • 25 回帖 • 8 关注
  • 快应用

    快应用 是基于手机硬件平台的新型应用形态;标准是由主流手机厂商组成的快应用联盟联合制定;快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台;以平台化的生态模式对个人开发者和企业开发者全品类开放。

    15 引用 • 127 回帖
  • NetBeans

    NetBeans 是一个始于 1997 年的 Xelfi 计划,本身是捷克布拉格查理大学的数学及物理学院的学生计划。此计划延伸而成立了一家公司进而发展这个商用版本的 NetBeans IDE,直到 1999 年 Sun 买下此公司。Sun 于次年(2000 年)六月将 NetBeans IDE 开源,直到现在 NetBeans 的社群依然持续增长。

    78 引用 • 102 回帖 • 724 关注
  • BAE

    百度应用引擎(Baidu App Engine)提供了 PHP、Java、Python 的执行环境,以及云存储、消息服务、云数据库等全面的云服务。它可以让开发者实现自动地部署和管理应用,并且提供动态扩容和负载均衡的运行环境,让开发者不用考虑高成本的运维工作,只需专注于业务逻辑,大大降低了开发者学习和迁移的成本。

    19 引用 • 75 回帖 • 702 关注
  • 星云链

    星云链是一个开源公链,业内简单的将其称为区块链上的谷歌。其实它不仅仅是区块链搜索引擎,一个公链的所有功能,它基本都有,比如你可以用它来开发部署你的去中心化的 APP,你可以在上面编写智能合约,发送交易等等。3 分钟快速接入星云链 (NAS) 测试网

    3 引用 • 16 回帖
  • 30Seconds

    📙 前端知识精选集,包含 HTML、CSS、JavaScript、React、Node、安全等方面,每天仅需 30 秒。

    • 精选常见面试题,帮助您准备下一次面试
    • 精选常见交互,帮助您拥有简洁酷炫的站点
    • 精选有用的 React 片段,帮助你获取最佳实践
    • 精选常见代码集,帮助您提高打码效率
    • 整理前端界的最新资讯,邀您一同探索新世界
    488 引用 • 384 回帖
  • OpenStack

    OpenStack 是一个云操作系统,通过数据中心可控制大型的计算、存储、网络等资源池。所有的管理通过前端界面管理员就可以完成,同样也可以通过 Web 接口让最终用户部署资源。

    10 引用 • 8 关注
  • frp

    frp 是一个可用于内网穿透的高性能的反向代理应用,支持 TCP、UDP、 HTTP 和 HTTPS 协议。

    17 引用 • 7 回帖 • 1 关注
  • Python

    Python 是一种面向对象、直译式电脑编程语言,具有近二十年的发展历史,成熟且稳定。它包含了一组完善而且容易理解的标准库,能够轻松完成很多常见的任务。它的语法简捷和清晰,尽量使用无异义的英语单词,与其它大多数程序设计语言使用大括号不一样,它使用缩进来定义语句块。

    561 引用 • 677 回帖 • 1 关注
  • jsDelivr

    jsDelivr 是一个开源的 CDN 服务,可为 npm 包、GitHub 仓库提供免费、快速并且可靠的全球 CDN 加速服务。

    5 引用 • 31 回帖 • 120 关注
  • 深度学习

    深度学习(Deep Learning)是机器学习的分支,是一种试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法。

    45 引用 • 44 回帖 • 2 关注
  • V2Ray
    1 引用 • 15 回帖 • 4 关注
  • Sublime

    Sublime Text 是一款可以用来写代码、写文章的文本编辑器。支持代码高亮、自动完成,还支持通过插件进行扩展。

    10 引用 • 5 回帖 • 1 关注
  • TensorFlow

    TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。

    20 引用 • 19 回帖
  • SEO

    发布对别人有帮助的原创内容是最好的 SEO 方式。

    36 引用 • 200 回帖 • 54 关注
  • 职场

    找到自己的位置,萌新烦恼少。

    127 引用 • 1708 回帖 • 1 关注
  • Wide

    Wide 是一款基于 Web 的 Go 语言 IDE。通过浏览器就可以进行 Go 开发,并有代码自动完成、查看表达式、编译反馈、Lint、实时结果输出等功能。

    欢迎访问我们运维的实例: https://wide.b3log.org

    30 引用 • 218 回帖 • 663 关注
  • Eclipse

    Eclipse 是一个开放源代码的、基于 Java 的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。

    76 引用 • 258 回帖 • 641 关注
  • 工具

    子曰:“工欲善其事,必先利其器。”

    308 引用 • 773 回帖
  • 浅吟主题

    Jeffrey Chen 制作的思源笔记主题,项目仓库:https://github.com/TCOTC/Whisper

    2 引用 • 34 回帖 • 1 关注
  • iOS

    iOS 是由苹果公司开发的移动操作系统,最早于 2007 年 1 月 9 日的 Macworld 大会上公布这个系统,最初是设计给 iPhone 使用的,后来陆续套用到 iPod touch、iPad 以及 Apple TV 等产品上。iOS 与苹果的 Mac OS X 操作系统一样,属于类 Unix 的商业操作系统。

    89 引用 • 150 回帖 • 1 关注
  • abitmean

    有点意思就行了

    44 关注
  • ZooKeeper

    ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 Google 的 Chubby 一个开源的实现,是 Hadoop 和 HBase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

    61 引用 • 29 回帖 • 14 关注
  • Windows

    Microsoft Windows 是美国微软公司研发的一套操作系统,它问世于 1985 年,起初仅仅是 Microsoft-DOS 模拟环境,后续的系统版本由于微软不断的更新升级,不但易用,也慢慢的成为家家户户人们最喜爱的操作系统。

    232 引用 • 484 回帖 • 1 关注
  • Scala

    Scala 是一门多范式的编程语言,集成面向对象编程和函数式编程的各种特性。

    13 引用 • 11 回帖 • 180 关注
  • ZeroNet

    ZeroNet 是一个基于比特币加密技术和 BT 网络技术的去中心化的、开放开源的网络和交流系统。

    1 引用 • 21 回帖 • 667 关注
  • Kubernetes

    Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。

    119 引用 • 54 回帖