【油猴脚本】优化知乎无限滚动时的内存占用(爬几千层楼的帖子用,防止浏览器卡住)

在这里插入图片描述

// ==UserScript==
// @name         知乎内存优化脚本
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  优化知乎无限滚动时的内存占用,提供轻量优化和完全移除两种模式
// @author       2byte
// @match        https://www.zhihu.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function() {
    'use strict';

    // 配置参数
    const CONFIG = {
        // 上方缓冲区大小(像素)
        topBuffer: 200,
        // 状态栏位置
        statusBarPosition: 'bottom-right',
        // 启用控制台日志
        debug: false
    };

    // 存储优化状态
    let optimizedAnswers = new Map();
    let statusBar = null;
    let currentMode = GM_getValue('zhihu_optimizer_mode', 'light'); // 'light' 或 'full'

    // 初始化
    function init() {
        log('初始化知乎内存优化脚本');
        createStatusBar();
        observeExistingAnswers();
        observeNewContent();
    }

    // 创建状态栏
    function createStatusBar() {
        statusBar = document.createElement('div');
        statusBar.id = 'zhihu-memory-optimizer-status';

        // 根据当前模式设置样式
        const isLightMode = currentMode === 'light';
        statusBar.className = isLightMode ? 'zhihu-optimizer-status-light' : 'zhihu-optimizer-status-full';

        statusBar.innerHTML = `
            <div class="zhihu-optimizer-status-header">
                <div class="zhihu-optimizer-status-title">知乎内存优化</div>
                <button class="zhihu-optimizer-mode-toggle" id="zhihuOptimizerModeToggle">
                    ${isLightMode ? '切换到完全移除模式' : '切换到轻量优化模式'}
                </button>
            </div>
            <div class="zhihu-optimizer-status-details">
                <div>已优化内容: <span id="zhihu-optimized-count">0</span> 个</div>
                <div>内存节省: <span id="zhihu-memory-saved">0</span> KB</div>
            </div>
            <div class="zhihu-optimizer-mode-explanation" id="zhihuOptimizerModeExplanation">
                ${isLightMode ?
                    '当前模式:轻量优化 - 内容被替换为占位符,可以快速恢复' :
                    '当前模式:完全移除 - 内容被完全删除,需要重新加载页面'}
            </div>
        `;

        // 样式
        Object.assign(statusBar.style, {
            position: 'fixed',
            bottom: '20px',
            right: '20px',
            color: 'white',
            padding: '10px 15px',
            borderRadius: '4px',
            boxShadow: '0 2px 10px rgba(0, 0, 0, 0.2)',
            fontSize: '14px',
            zIndex: '10000',
            minWidth: '300px',
            backgroundColor: isLightMode ? '#0084ff' : '#ff4d4f'
        });

        // 添加模式切换事件
        const modeToggle = statusBar.querySelector('#zhihuOptimizerModeToggle');
        modeToggle.addEventListener('click', toggleMode);

        document.body.appendChild(statusBar);

        // 添加CSS样式
        addStyles();
    }

    // 添加CSS样式
    function addStyles() {
        const style = document.createElement('style');
        style.textContent = `
            .zhihu-optimizer-status-header {
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 8px;
            }

            .zhihu-optimizer-status-title {
                font-weight: bold;
            }

            .zhihu-optimizer-mode-toggle {
                background: rgba(255, 255, 255, 0.2);
                border: none;
                color: white;
                padding: 4px 8px;
                border-radius: 4px;
                cursor: pointer;
                font-size: 12px;
            }

            .zhihu-optimizer-status-details {
                display: flex;
                justify-content: space-between;
                font-size: 12px;
                opacity: 0.9;
            }

            .zhihu-optimizer-mode-explanation {
                margin-top: 10px;
                padding: 10px;
                background: rgba(255, 255, 255, 0.1);
                border-radius: 4px;
                font-size: 12px;
            }

            .zhihu-optimized-light {
                background-color: #f5f5f5 !important;
                border: 1px dashed #d9d9d9 !important;
            }

            .zhihu-optimized-full {
                background-color: #fff2f0 !important;
                border: 1px dashed #ffa39e !important;
            }

            .zhihu-optimizer-placeholder-light {
                padding: 20px;
                text-align: center;
                color: #8590a6;
                background-color: #f6f6f6;
                border: 1px dashed #d9d9d9;
                border-radius: 4px;
            }

            .zhihu-optimizer-placeholder-full {
                padding: 20px;
                text-align: center;
                color: #ff4d4f;
                background-color: #fff2f0;
                border: 1px dashed #ffa39e;
                border-radius: 4px;
            }

            .zhihu-optimizer-placeholder-light button,
            .zhihu-optimizer-placeholder-full button {
                background-color: #0084ff;
                color: white;
                border: none;
                padding: 6px 12px;
                border-radius: 4px;
                cursor: pointer;
                margin-top: 10px;
            }

            .zhihu-optimizer-placeholder-full button {
                background-color: #ff4d4f;
            }
        `;
        document.head.appendChild(style);
    }

    // 切换模式
    function toggleMode() {
        if (currentMode === 'light') {
            // 切换到完全移除模式
            currentMode = 'full';
            GM_setValue('zhihu_optimizer_mode', 'full');

            // 更新状态栏
            statusBar.style.backgroundColor = '#ff4d4f';
            const modeToggle = statusBar.querySelector('#zhihuOptimizerModeToggle');
            modeToggle.textContent = '切换到轻量优化模式';
            const modeExplanation = statusBar.querySelector('#zhihuOptimizerModeExplanation');
            modeExplanation.textContent = '当前模式:完全移除 - 内容被完全删除,需要重新加载页面';

            // 将所有已优化的内容转换为完全移除模式
            optimizedAnswers.forEach((data, answerId) => {
                const answer = document.querySelector(`[data-optimizer-id="${answerId}"]`);
                if (answer && data.type === 'light') {
                    completelyRemoveAnswer(answer);
                }
            });
        } else {
            // 切换到轻量优化模式
            currentMode = 'light';
            GM_setValue('zhihu_optimizer_mode', 'light');

            // 更新状态栏
            statusBar.style.backgroundColor = '#0084ff';
            const modeToggle = statusBar.querySelector('#zhihuOptimizerModeToggle');
            modeToggle.textContent = '切换到完全移除模式';
            const modeExplanation = statusBar.querySelector('#zhihuOptimizerModeExplanation');
            modeExplanation.textContent = '当前模式:轻量优化 - 内容被替换为占位符,可以快速恢复';

            // 注意:在完全移除模式下,我们无法恢复内容,所以这里只是更新状态栏
            log('切换到轻量优化模式,但无法恢复已完全移除的内容');
        }

        updateStatusBar();
    }

    // 更新状态栏
    function updateStatusBar() {
        const countElement = document.getElementById('zhihu-optimized-count');
        const memoryElement = document.getElementById('zhihu-memory-saved');

        if (countElement && memoryElement) {
            let totalMemory = 0;
            optimizedAnswers.forEach(data => {
                totalMemory += data.size;
            });

            countElement.textContent = optimizedAnswers.size;
            memoryElement.textContent = totalMemory.toFixed(1);
        }
    }

    // 观察现有回答
    function observeExistingAnswers() {
        const answers = getAnswerElements();

        answers.forEach(answer => {
            if (!answer.dataset.optimizerId) {
                const id = generateId();
                answer.dataset.optimizerId = id;
                observeAnswer(answer);
            }
        });

        log(`观察了 ${answers.length} 个现有回答`);
    }

    // 观察新加载的内容
    function observeNewContent() {
        const observer = new MutationObserver(mutations => {
            mutations.forEach(mutation => {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === 1) { // Element node
                        // 检查是否是回答容器
                        const answers = node.querySelectorAll && node.querySelectorAll('.List-item, .Question-main .ListShortcut .List-item');
                        if (answers && answers.length) {
                            answers.forEach(answer => {
                                if (!answer.dataset.optimizerId) {
                                    const id = generateId();
                                    answer.dataset.optimizerId = id;
                                    observeAnswer(answer);
                                }
                            });
                            log(`观察了 ${answers.length} 个新回答`);
                        }

                        // 如果节点本身是回答
                        if (node.matches && (node.matches('.List-item') || node.matches('.Question-main .ListShortcut .List-item'))) {
                            if (!node.dataset.optimizerId) {
                                const id = generateId();
                                node.dataset.optimizerId = id;
                                observeAnswer(node);
                                log('观察了 1 个新回答');
                            }
                        }
                    }
                });
            });
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    // 监测单个回答的可见性
    function observeAnswer(answer) {
        const observer = new IntersectionObserver(entries => {
            entries.forEach(entry => {
                // 如果回答在视窗上方且不可见,进行优化
                if (!entry.isIntersecting && entry.boundingClientRect.top < -CONFIG.topBuffer) {
                    if (currentMode === 'light') {
                        optimizeAnswer(answer);
                    } else {
                        completelyRemoveAnswer(answer);
                    }
                }

                // 如果回答进入视窗,且是轻量优化模式,恢复内容
                if (entry.isIntersecting && currentMode === 'light' && optimizedAnswers.has(answer.dataset.optimizerId)) {
                    const data = optimizedAnswers.get(answer.dataset.optimizerId);
                    if (data && data.type === 'light') {
                        restoreAnswer(answer);
                    }
                }
            });
        }, {
            root: null,
            threshold: 0,
            rootMargin: `${CONFIG.topBuffer}px 0px 0px 0px`
        });

        observer.observe(answer);
    }

    // 轻量优化回答内容
    function optimizeAnswer(answer) {
        if (optimizedAnswers.has(answer.dataset.optimizerId)) {
            return; // 已经优化过了
        }

        // 存储原始内容
        const originalHTML = answer.innerHTML;
        const estimatedSize = estimateMemoryUsage(originalHTML);

        optimizedAnswers.set(answer.dataset.optimizerId, {
            html: originalHTML,
            size: estimatedSize,
            type: 'light'
        });

        // 创建占位符
        const placeholder = document.createElement('div');
        placeholder.className = 'zhihu-optimizer-placeholder-light';
        placeholder.innerHTML = `
            <p>此内容已优化以节省内存</p>
            <p style="font-size: 12px; margin-top: 5px;">约节省 ${estimatedSize}KB 内存</p>
            <button>恢复内容</button>
        `;

        // 添加恢复按钮事件
        const restoreButton = placeholder.querySelector('button');
        restoreButton.addEventListener('click', () => {
            restoreAnswer(answer);
        });

        // 替换内容
        answer.innerHTML = '';
        answer.appendChild(placeholder);

        // 添加优化标记
        answer.classList.add('zhihu-optimized-light');

        log(`轻量优化了回答 ${answer.dataset.optimizerId},节省约 ${estimatedSize}KB 内存`);
        updateStatusBar();
    }

    // 完全移除回答内容
    function completelyRemoveAnswer(answer) {
        if (optimizedAnswers.has(answer.dataset.optimizerId)) {
            // 如果已经是轻量优化,先清除之前的优化数据
            optimizedAnswers.delete(answer.dataset.optimizerId);
        }

        const originalHTML = answer.innerHTML;
        const estimatedSize = estimateMemoryUsage(originalHTML);

        // 存储基本信息(在完全移除模式下,我们不存储完整HTML)
        optimizedAnswers.set(answer.dataset.optimizerId, {
            size: estimatedSize,
            type: 'full'
        });

        // 创建占位符
        const placeholder = document.createElement('div');
        placeholder.className = 'zhihu-optimizer-placeholder-full';
        placeholder.innerHTML = `
            <p>此内容已完全移除以节省内存</p>
            <p style="font-size: 12px; margin-top: 5px;">约节省 ${estimatedSize}KB 内存</p>
            <button>重新加载页面</button>
        `;

        // 添加重新加载按钮事件
        const reloadButton = placeholder.querySelector('button');
        reloadButton.addEventListener('click', () => {
            location.reload();
        });

        // 替换内容
        answer.innerHTML = '';
        answer.appendChild(placeholder);

        // 添加优化标记
        answer.classList.remove('zhihu-optimized-light');
        answer.classList.add('zhihu-optimized-full');

        log(`完全移除了回答 ${answer.dataset.optimizerId},节省约 ${estimatedSize}KB 内存`);
        updateStatusBar();
    }

    // 恢复回答内容(仅适用于轻量优化模式)
    function restoreAnswer(answer) {
        const data = optimizedAnswers.get(answer.dataset.optimizerId);
        if (data && data.type === 'light') {
            answer.innerHTML = data.html;
            answer.classList.remove('zhihu-optimized-light');
            optimizedAnswers.delete(answer.dataset.optimizerId);

            log(`恢复了回答 ${answer.dataset.optimizerId}`);
            updateStatusBar();

            // 重新观察这个回答
            observeAnswer(answer);
        }
    }

    // 获取回答元素
    function getAnswerElements() {
        return document.querySelectorAll('.List-item, .Question-main .ListShortcut .List-item');
    }

    // 估算内存使用量
    function estimateMemoryUsage(html) {
        // 简化估算:每个字符大约占用2字节(UTF-16)
        const size = html.length * 2 / 1024;
        return Math.round(size * 10) / 10; // 保留一位小数
    }

    // 生成唯一ID
    function generateId() {
        return 'optimizer_' + Math.random().toString(36).substr(2, 9);
    }

    // 日志函数
    function log(message) {
        if (CONFIG.debug) {
            console.log(`[知乎内存优化] ${message}`);
        }
    }

    // 页面加载完成后初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();```

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值