480 lines
12 KiB
Plaintext
480 lines
12 KiB
Plaintext
---
|
||
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` 进行调试输出,支持不同级别
|
||
- 在开发阶段启用详细日志
|
||
- 测试各种边界情况和错误场景
|
||
- 验证用户配置的有效性
|
||
|
||
- 测试网络请求的超时和错误处理 |