本文目录导读:

- 浏览器用户脚本(Tampermonkey / Greasemonkey 等)
- Node.js 模块
- 浏览器扩展(Chrome/Firefox)
- 通用的清理原则
- 如果脚本没有提供清理功能(最坏情况)
- 总结:最好的清理是“设计时考虑清理”
在脚本环境中,“卸载脚本”通常指移除脚本加载的全局变量、事件监听、定时器、DOM元素修改等副作用,要“清理干净”,需要根据脚本的类型(如用户脚本、Node.js模块、浏览器扩展等)采取不同的策略,以下是几种常见场景的清理方法:
浏览器用户脚本(Tampermonkey / Greasemonkey 等)
用户脚本通常通过 @grant 声明权限,要完全清理卸载,需要还原脚本所做的所有修改。
脚本内部应提供卸载函数:
// ==UserScript==
// @name My Script
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// ==/UserScript==
// 存储所有副作用,便于卸载清理
const cleanups = [];
// 示例1: 添加全局样式
const style = GM_addStyle('.my-script-style { color: red; }');
cleanups.push(() => style.remove());
// 示例2: 添加事件监听
const handler = () => console.log('clicked');
document.addEventListener('click', handler);
cleanups.push(() => document.removeEventListener('click', handler));
// 示例3: 设置定时器
const timer = setInterval(() => {}, 1000);
cleanups.push(() => clearInterval(timer));
// 示例4: 修改DOM(需记录原始状态)
const originalContent = document.body.innerHTML;
cleanups.push(() => { document.body.innerHTML = originalContent; });
// 卸载函数
window.myScriptCleanup = function() {
cleanups.forEach(fn => fn());
cleanups.length = 0; // 清空数组
console.log('Script cleaned up');
};
// 在菜单中添加卸载命令(如果支持)
GM_registerMenuCommand('Uninstall Script', () => {
window.myScriptCleanup();
});
手动清理:如果脚本没有提供卸载函数,需要手动检查并移除:
- 检查
document.head中是否有脚本注入的<style>或<link>。 - 移除脚本添加的全局变量(如
window.myScriptData)。 - 清空定时器(通过
console.log或开发者工具查看setInterval的ID)。 - 移除事件监听(在开发者工具的 Event Listeners 面板中查找)。
Node.js 模块
Node.js 中卸载模块通常指从 require.cache 中清除,并释放其副作用(如全局变量、定时器、数据库连接等)。
模块内部清理:
// my-module.js
let timer;
module.exports = {
start() {
timer = setInterval(() => {}, 1000);
},
// 提供清理方法
cleanup() {
clearInterval(timer);
// 关闭数据库连接、文件描述符等
console.log('Module cleaned up');
}
};
外部强制卸载:
// 强制从缓存中删除模块
const modulePath = require.resolve('./my-module.js');
delete require.cache[modulePath];
// 但模块内的定时器仍然存在!需要手动调用清理
const myModule = require('./my-module.js');
myModule.cleanup();
更好的做法:使用 process.on('exit') 或 process.on('SIGINT') 自动清理:
process.on('exit', () => {
clearInterval(timer);
// 其他清理
});
浏览器扩展(Chrome/Firefox)
扩展卸载时,浏览器会自动清理大部分副作用(如后台页面、内容脚本),但持久化数据(如 chrome.storage、IndexedDB、Cookies)不会被自动清除。
清理持久化数据:
-
在扩展的
background.js中添加卸载监听:// 监听扩展被卸载(仅 Chrome 支持) chrome.runtime.onSuspend.addListener(() => { // 清理存储 chrome.storage.local.clear(); // 清理 IndexedDB indexedDB.deleteDatabase('MyDB'); // 清理 Cookies(需指定域名) chrome.cookies.remove({ url: 'https://example.com', name: 'session' }); }); -
或者让用户手动清除:在扩展选项中提供“清除所有数据”按钮。
通用的清理原则
无论哪种脚本,清理都应该遵循以下步骤:
- 记录所有副作用:在脚本初始运行时,保存所有被修改的状态、创建的对象、注册的回调。
- 提供反函数:每个副作用都对应一个还原操作(如
addEventListener→removeEventListener,setTimeout→clearTimeout)。 - 提供统一的清理接口:如
cleanup()、destroy()或全局变量__cleanup__。 - 事件监听器:使用有名字的函数(不要用匿名函数)以便移除。
- 定时器:保存所有定时器ID,统一
clearInterval/clearTimeout。 - DOM修改:如果修改了很多内容,最好在开始时保存一份原始 DOM 快照(
document.body.cloneNode(true)),清理时恢复。
如果脚本没有提供清理功能(最坏情况)
- 对于用户脚本:禁用或卸载该脚本(在Tampermonkey中点击“移除”),然后刷新页面——大部分副作用会随页面重载消失。
- 对于Node.js:关闭进程(
Ctrl+C),所有定时器和监听器都会随进程终止。 - 对于浏览器扩展:在
chrome://extensions中移除扩展,刷新所有受影响页面。
最好的清理是“设计时考虑清理”
- 写脚本时:始终提供
cleanup()函数,并遵循“副作用记录+反向操作”模式。 - 卸载时:优先调用脚本提供的清理函数,然后刷新页面或重启进程。
- 持久数据:需要手动删除存储(存储、数据库、cookies)。