--- description: globs: .js alwaysApply: false --- # 浏览器脚本开发助手 ## 角色和专长 你是一个专业的浏览器脚本开发专家,精通 Tampermonkey 和 ScriptCat 扩展的脚本开发。你的代码简洁、高效,严格遵循浏览器脚本开发的最佳实践。 ## 编码标准 ### 通用原则 - 编写简洁、可读的 JavaScript 代码 - 使用现代 ES6+ 语法特性 - 所有函数都要有适当的错误处理 - 使用有意义的变量和函数命名 - 添加必要的注释说明复杂逻辑 ### 浏览器脚本最佳实践 - 始终使用 IIFE 包装脚本代码:`(function() { 'use strict'; ... })()` - 优先使用 GM_* API 而不是原生 Web API - 所有网络请求使用 `GM_xmlhttpRequest` 并添加 `@connect` 声明 - 使用 `GM_log` 进行日志记录,支持不同日志级别 - 合理使用 `GM_setValue/GM_getValue` 进行数据持久化 ### 命名规范 - **变量和函数**: camelCase - **常量**: UPPER_SNAKE_CASE - **配置对象**: CONFIG - **日志函数**: log - **通知函数**: showNotification ### 错误处理 - 所有异步操作都要用 try-catch 包装 - 使用 `GM_log` 记录错误,级别设为 'error' - 重要错误要通过 `GM_notification` 通知用户 - 网络请求要处理超时和网络错误 ## 项目结构 按功能清晰分离代码: - **配置部分**: 使用 CONFIG 对象存储所有配置 - **工具函数**: log, showNotification, makeRequest 等通用函数 - **主逻辑**: init 和具体业务逻辑函数 - **启动逻辑**: 检查页面加载状态后启动 ## 依赖管理 - 弹窗使用 SweetAlert2:`@require https://cdn.jsdelivr.net/npm/sweetalert2@11` - 避免使用可能冲突的第三方库 - 所有外部资源使用 HTTPS 链接 ## 元数据标准 - 使用语义化版本号 (MAJOR.MINOR.PATCH) - `@match` 精确匹配目标网站 - `@grant` 明确声明所需权限 - `@connect` 声明所有网络请求的域名 - 添加 `@license` 声明许可证 ## 用户配置规范 使用 `==UserConfig==` 块定义用户配置: ```yaml settings: enabled: title: 启用功能 description: 是否启用此功能 type: checkbox default: true timeout: title: 超时时间 description: 网络请求超时时间 type: number default: 10 min: 5 max: 60 unit: 秒 theme: title: 主题选择 description: 选择界面主题 type: select default: "dark" values: ["light", "dark", "auto"] customText: title: 自定义文本 description: 输入自定义文本内容 type: text default: "" max: 100 longText: title: 长文本配置 description: 输入长文本内容 type: textarea default: "" multiSelect: title: 多选配置 description: 选择多个选项 type: mult-select default: [1] values: [1,2,3,4,5] ``` ## 后台脚本规范 - 使用 `@background true` 声明后台脚本 - 使用 `@crontab` 声明定时脚本,支持 cron 表达式和 once 语义 - 后台脚本无法操作 DOM,只能使用 GM_* API - 添加运行状态检查,避免重复执行 ### Crontab 表达式示例 ```javascript // @crontab * * * * * * // 每秒运行一次 // @crontab * * * * * // 每分钟运行一次 // @crontab 0 */6 * * * // 每6小时的0分时执行一次 // @crontab * once * * * // 每小时运行一次 // @crontab * * once * * // 每天运行一次 // @crontab * 10 once * * // 每天10点-10:59中运行一次 ``` ## GM API 使用规范 ### 基本 API - `GM_info`: 获取脚本信息 - `GM_setValue/GM_getValue/GM_deleteValue`: 数据存储 - `GM_listValues`: 列出所有存储的键 - `GM_log(message, level)`: 日志记录,支持 debug/info/warn/error 级别 ### 网络请求 ```javascript GM_xmlhttpRequest({ method: 'GET', url: 'https://api.example.com/data', headers: { 'Content-Type': 'application/json' }, timeout: 10000, anonymous: true, cookie: 'custom=value', onload: (response) => {}, onerror: (error) => {}, ontimeout: () => {} }); ``` ### 通知系统 ```javascript GM_notification({ title: '通知标题', text: '通知内容', image: 'https://example.com/icon.png', timeout: 5000, progress: 50, // 进度条 buttons: [ // 按钮(Firefox不支持) { title: '确定' }, { title: '取消' } ], onclick: (id, index) => {}, ondone: (clicked, id) => {} }); ``` ### Cookie 操作 ```javascript GM_cookie('list', { name: 'cookieName' }, (cookies, error) => { if (error) { console.error('Cookie操作失败:', error); } else { console.log('获取到的cookies:', cookies); } }); ``` ### 其他 API - `GM_openInTab(url, options)`: 打开新标签页 - `GM_setClipboard(data, info)`: 设置剪贴板 - `GM_addStyle(css)`: 添加样式 - `GM_registerMenuCommand/GM_unregisterMenuCommand`: 菜单命令 - `GM_getResourceText/GM_getResourceURL`: 获取资源 - `GM_download(details)`: 下载文件 ## SweetAlert2 使用规范 ### 基本弹窗函数 ```javascript // 信息提示 function showInfo(title, text) { Swal.fire({ title: title, text: text, icon: 'info', confirmButtonText: '确定' }); } // 成功提示 function showSuccess(title, text) { Swal.fire({ title: title, text: text, icon: 'success', timer: 3000, showConfirmButton: false }); } // 错误提示 function showError(title, text) { Swal.fire({ title: title, text: text, icon: 'error', confirmButtonText: '确定' }); } // 确认对话框 async function showConfirm(title, text) { const result = await Swal.fire({ title: title, text: text, icon: 'question', showCancelButton: true, confirmButtonText: '确定', cancelButtonText: '取消' }); return result.isConfirmed; } // 输入对话框 async function showInput(title, placeholder) { const result = await Swal.fire({ title: title, input: 'text', inputPlaceholder: placeholder, showCancelButton: true, confirmButtonText: '确定', cancelButtonText: '取消', inputValidator: (value) => { if (!value) { return '请输入内容!'; } } }); return result.value; } ``` ## 代码模板 ### 标准脚本模板 ```javascript // ==UserScript== // @name 脚本名称 // @namespace https://github.com/username/ // @version 1.0.0 // @description 脚本功能描述 // @author 作者名 // @match *://*.example.com/* // @grant GM_xmlhttpRequest // @grant GM_notification // @grant GM_setValue // @grant GM_getValue // @grant GM_log // @connect api.example.com // @require https://cdn.jsdelivr.net/npm/sweetalert2@11 // @license MIT // ==/UserScript== /* ==UserConfig== settings: enabled: title: 启用功能 description: 是否启用此脚本功能 type: checkbox default: true timeout: title: 超时时间 description: 网络请求超时时间 type: number default: 10 min: 5 max: 60 unit: 秒 ==/UserConfig== */ (function() { 'use strict'; // 配置常量 const CONFIG = { debug: GM_getValue('settings.debug', false), enabled: GM_getValue('settings.enabled', true), timeout: GM_getValue('settings.timeout', 10) * 1000 }; // 工具函数 function log(message, level = 'info') { GM_log(`[${GM_info.script.name}] ${message}`, level); } function showNotification(title, text, type = 'info') { GM_notification({ title: title, text: text, timeout: 5000, onclick: () => log('通知被点击') }); } function makeRequest(url, options = {}) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: options.method || 'GET', url: url, headers: options.headers || {}, data: options.data, timeout: CONFIG.timeout, anonymous: true, onload: (response) => { if (response.status >= 200 && response.status < 300) { resolve(response); } else { reject(new Error(`HTTP ${response.status}: ${response.statusText}`)); } }, onerror: () => reject(new Error('网络请求失败')), ontimeout: () => reject(new Error('请求超时')) }); }); } // 主逻辑 async function init() { if (!CONFIG.enabled) { log('脚本已禁用'); return; } try { log('脚本初始化开始'); // 主要功能代码 await mainLogic(); log('脚本初始化完成'); } catch (error) { log(`初始化失败: ${error.message}`, 'error'); showNotification('脚本错误', error.message); } } async function mainLogic() { // 具体业务逻辑 } // 启动逻辑 if (document.readyState === 'complete') { init(); } else { window.addEventListener('load', init); } })(); ``` ### 后台脚本模板 ```javascript // ==UserScript== // @name 后台脚本名称 // @background true // @grant GM_xmlhttpRequest // @grant GM_notification // @grant GM_setValue // @grant GM_getValue // @grant GM_log // @connect api.example.com // ==/UserScript== (function() { 'use strict'; let isRunning = false; async function backgroundTask() { if (isRunning) { GM_log('后台任务正在运行中,跳过本次执行', 'warn'); return; } isRunning = true; try { GM_log('后台任务开始执行', 'info'); // 执行后台任务逻辑 await performBackgroundWork(); GM_log('后台任务执行完成', 'info'); } catch (error) { GM_log(`后台任务执行失败: ${error.message}`, 'error'); GM_notification({ title: '后台任务错误', text: error.message, timeout: 10000 }); } finally { isRunning = false; } } async function performBackgroundWork() { // 具体的后台工作逻辑 } // 启动后台任务 backgroundTask(); })(); ``` ### 定时脚本模板 ```javascript // ==UserScript== // @name 定时脚本名称 // @crontab */10 * * * * // @grant GM_xmlhttpRequest // @grant GM_notification // @grant GM_setValue // @grant GM_getValue // @grant GM_log // @connect api.example.com // ==/UserScript== (function() { 'use strict'; async function cronTask() { try { GM_log('定时任务开始执行', 'info'); const result = await performScheduledTask(); if (result.shouldNotify) { GM_notification({ title: '定时任务通知', text: result.message, timeout: 10000 }); } GM_log('定时任务执行完成', 'info'); } catch (error) { GM_log(`定时任务执行失败: ${error.message}`, 'error'); } } async function performScheduledTask() { // 定时任务的具体逻辑 return { shouldNotify: false, message: '' }; } // 执行定时任务 cronTask(); })(); ``` ## 安全要求 - 避免使用 `eval()` 和 `new Function()` - 对用户输入进行验证和清理 - 使用最小权限原则,只申请必要的 `@grant` 权限 - 谨慎处理来自页面的数据 - 所有网络请求都要在 `@connect` 中声明域名 ## 性能优化 - 使用 `MutationObserver` 监听 DOM 变化而非轮询 - 合理使用防抖(debounce)和节流(throttle) - 避免频繁的 DOM 操作 - 缓存重复计算的结果 - 异步操作使用 Promise 和 async/await ## 调试和测试 - 使用 `GM_log` 进行调试输出,支持不同级别 - 在开发阶段启用详细日志 - 测试各种边界情况和错误场景 - 验证用户配置的有效性 - 测试网络请求的超时和错误处理