一个旨在帮助开发者快速构建功能强大、可维护的现代油猴脚本的框架。它提供模块化架构、自动化的UI设置面板、响应式生命周期管理、高性能DOM调度器与网络请求拦截器等核心功能,让您专注于实现创意,而非繁琐的底层细节。
이 스크립트는 직접 설치하는 용도가 아닙니다. 다른 스크립트에서 메타 지시문 // @require https://update.greasyfork.org/scripts/545792/1908492/Caliberjs%20Framework%20Library.js을(를) 사용하여 포함하는 라이브러리입니다.
Caliber.js 是一个专为现代油猴(Userscript)脚本打造的高性能、模块化、响应式开发框架。它将复杂的底层细节(异步生命周期流转、并发排他队列、DOM分帧调度、双 Hub 管道式网络请求拦截、配置自愈、防不法篡改与防删防护、CSP/Trusted-Types 兼容、SPA路由感知等)进行了高度工程化封装,让开发者只需专注于业务逻辑实现。
在油猴脚本头部引入 Caliber.js(或通过 @require 引入),定义继承自 Caliber.Module 的模块类并调用 Caliber.createApp 启动:
// ==UserScript==
// @name My Advanced Script
// @namespace https://example.com/
// @version 1.0.0
// @match https://example.com/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// @grant unsafeWindow
// @require https://path/to/Caliber.js
// ==/UserScript==
// 1. 定义功能模块
class MyFeatureModule extends Caliber.Module {
id = "myFeature";
name = "核心增强功能";
description = "自动修改特定节点样式并显示状态。";
defaultConfig = {
enabled: true,
highlightColor: {
type: "color",
value: "#ff0000",
label: "高亮颜色",
description: "设置被修饰元素的背景颜色"
},
customRules: {
type: "textarea",
value: "rule1\nrule2",
label: "自定义匹配规则",
inputProps: { rows: 3, placeholder: "每行一条规则..." }
}
};
async onEnable(context) {
this._logger.log("模块已开启,当前 URL 参数:", context.query);
}
async onDisable() {
this._logger.log("模块已关闭,异步资源清理完成。");
}
}
// 2. 启动框架内核
(async () => {
await Caliber.createApp({
appName: "我的增强脚本",
isDebug: true, // 开启调试模式与内存泄露 Auditor 监控
modules: [MyFeatureModule],
services: {
storage: {
get: async () => JSON.parse((await GM_getValue("app_config", "{}"))),
set: async (val) => GM_setValue("app_config", JSON.stringify(val))
},
command: {
register: (name, cb) => GM_registerMenuCommand(name, cb)
}
},
settingsPanel: {
bottom: 50,
right: 0 // 贴在屏幕右边缘
}
});
})();
createApp)Caliber.createApp(options) 是脚本唯一的应用入口,内置了防重复启动预检(Preflight)与沙箱环境自适应机制。
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
appName |
string |
是 | - | 应用唯一名称,用于生成命名空间、日志前缀、设置面板标题及菜单项 |
modules |
Array<typeof Module> |
是 | - | 注册的模块类数组(传类本身,而非实例) |
services |
object |
是 | - | 宿主环境适配器服务集合 |
services.storage |
StorageAdapter |
否 | localStorage | 包含 get(): Promise<object> 和 set(val): Promise<void> |
services.command |
CommandAdapter |
否 | 空实现 | 包含 register(name, callback): void |
services.hostWindow |
Window |
否 | 自动识别 | 默认自动识别 unsafeWindow,无油猴环境回退至标准 window |
services.hostDocument |
Document |
否 | document |
宿主 document 对象 |
services.style |
StyleAdapter |
否 | 自动安全注入 | 样式注入适配器,提供 add(css, id) 与 remove(id) |
isDebug |
boolean |
否 | false |
是否开启 Debug 模式(打印详细日志、激活 Auditor 泄漏审计) |
settingsPanelEnabled |
boolean |
否 | true |
首次启动时设置面板触发按钮是否默认开启 |
settingsPanel |
object |
否 | {} |
设置面板外观及触发按钮定位配置,详见下方 |
framework.domProcessorBatchSize |
number |
否 | 20 |
DOM 调度器每一渲染帧(rAF)允许处理的最大节点数 |
settingsPanel: {
enabled: true, // 是否启用按钮
bottom: 80, // 距离屏幕底部像素值 (px)
right: 0 // 距离屏幕右侧像素值 (px)
}
💡 智能动态贴边圆角: 框架会根据
right和bottom坐标自动计算最佳形态:
right: 0, bottom: 50➔ 右侧无圆角、平贴边缘(经典胶囊半圆);right: 20, bottom: 0➔ 底部无圆角、平贴下沿;right: 0, bottom: 0➔ 右侧与底部皆为平角,完美卡在屏幕右下死角;right: 25, bottom: 25➔ 自动转化为四角圆润的独立悬浮按钮。
Module Base Class)所有功能模块必须继承 Caliber.Module 类。
class DemoModule extends Caliber.Module {
id = "demo-module"; // 唯一标识符 (必填,不可使用 "base-module")
name = "示范模块"; // UI 中显示的模块名称 (必填)
description = "这是一个示范模块"; // UI 中显示的描述文本
match = "/user/:id"; // (可选) 页面匹配规则,不匹配时不激活
uiGuard = null; // (可选) 声明式 UI 守护配置,详见第 5 节
}
defaultConfig Schema)defaultConfig 支持简单的基本类型值,也支持丰富的 Schema 配置(框架根据 Schema 自动生成 Shadow DOM 设置面板)。
defaultConfig = {
enabled: true, // 框架保留字段:控制模块启停(必须包含或默认赋予)
// 1. 文本输入框 (string)
apiKey: {
type: "string",
value: "default_token",
label: "API 密钥",
description: "用户访问鉴权 Token",
inputProps: { placeholder: "请输入密钥..." }
},
// 2. 多行文本域 (textarea) - v1.2.0 新增
blockKeywords: {
type: "textarea",
value: "关键词A\n关键词B",
label: "屏蔽列表",
description: "每行输入一个过滤关键词",
inputProps: { rows: 4, placeholder: "一行一条..." },
divider: "bottom" // 底部添加分割线
},
// 3. 数字输入框 (number)
limitCount: {
type: "number",
value: 20,
label: "单页显示数量",
inputProps: { min: 1, max: 100, step: 5 }
},
// 4. 开关切换器 (boolean)
autoRefresh: {
type: "boolean",
value: false,
label: "自动刷新开关"
},
// 5. 下拉选择器 (select)
displayMode: {
type: "select",
value: "compact",
label: "排版布局",
options: [
{ label: "紧凑视图", value: "compact" },
{ label: "松散视图", value: "comfortable" }
]
},
// 6. 颜色选择器 (color)
themeColor: {
type: "color",
value: "#007aff",
label: "高亮主题色"
}
};
indentLevel (1 | 2 | 3): 树状层级缩进,自动生成等宽代码字体 └─ 连接线。divider ("top" | "bottom" | "both"): 在配置项周围添加视觉分割线。inputProps (object): 标签属性透传,可设置 min, max, step, placeholder, rows, maxLength 等原生 HTML 属性。Caliber v1.2.0 全面支持 Promise 异步返回。所有生命周期流转均受内部 排他异步串行队列 (#runExclusive) 保护,杜绝并发竞态。
| 生命周期方法 | 支持异步 | 触发时机 | 传入参数 |
|---|---|---|---|
onEnable(context) |
是 | 模块被启用(且页面规则匹配通过)时调用 | context: { params: {}, query: {} } |
onDisable() |
是 | 模块被禁用、或页面切换至不匹配页面时调用。 必须在此清理所有事件监听器和定时器 |
无 |
onConfigChange(key, newVal, oldVal) |
否 | 用户在设置面板中修改该模块配置时实时触发 | key, newVal, oldVal |
onNavigate(context) |
是 | SPA 页面专用。模块处于激活状态且页面发生前端路由导航时触发 | context: { params: {}, query: {} } |
onRender(targetElement) |
否 | UI Guardian 专用。DOM 修复注入 UI 时触发 | targetElement: 目标挂载父容器 |
onCleanup() |
否 | UI Guardian 专用。组件被卸载时触发 | 无 |
在模块内部通过 this.xxx 可直接调用核心能力:
this._logger // 日志服务实例 (带有模块 ID 样式化彩色前缀)
this._eventBus // 全局事件总线 (on, off, emit)
this._hostWindow // 宿主 window 对象 (unsafeWindow)
this._hostDocument // 宿主 document 对象
this._scheduler // 高性能 DOM 分帧批量处理调度器
this._interceptor // 宿主网络请求拦截器
this._sanitizer // DOM 净化与安全注入服务
this._executor // 页面作用域代码执行器 (带10秒熔断保护)
this._utils // 实用工具集合 (如 checkMatch 路由分词)
this._module // 模块自控门面 (如请求热重启 this._module.requestReset())
_scheduler)比传统 MutationObserver 性能更优。采用分帧批量调度(默认每帧处理 20 个节点),具备零订阅自动休眠机制。
register)const taskId = this._scheduler.register(selector, callback, options);
selector (string): 目标 CSS 选择器。callback ((node: HTMLElement) => void): 匹配节点时的回调。options (DomProcessorOptions, 可选):
add: boolean (默认 true) 是否监听节点新增。attributes: boolean (默认 false) 是否监听属性变更。attributeFilter: string[] 指定关注的属性名列表(如 ["data-status", "class"])。root: HTMLElement | string 限制只监听特定父容器内部。processExisting: boolean (默认 false) 是否在注册时立即处理 DOM 中已存在的匹配节点。unregister)this._scheduler.unregister(taskId);
_interceptor)依托宿主全局单例 Hub(__CALIBER_PAGE_NETWORK_HUB_V1__),同时支持 Fetch 和 XMLHttpRequest (XHR),具备管道式 FIFO 链式调度与跨模块多回调响应:
this._interceptor
.target({
url: "https://api.example.com/v1/user/info", // 目标 URL (Origin + Pathname)
method: "GET", // HTTP 方法 (GET/POST 等)
match: "/user/*" // 生效页面限制规则
})
.onRequest(`
// 运行在宿主页面真实上下文中 (无闭包纯字符串)
// 可用上下文: url, config, urlObject
urlObject.searchParams.set('injected_by', 'caliber');
config.headers = { ...config.headers, 'X-Caliber-Token': 'Verified' };
return { url: urlObject.toString(), config };
`)
.onResponse((responseData) => {
// 运行在油猴沙箱上下文中,安全接收并解析返回的数据 (JSON 或 text)
console.log("沙箱成功捕获响应数据:", responseData);
})
.register(this.id);
this._interceptor.removeHook(this.id);
_executor)突破油猴沙箱(Sandbox)限制,在宿主真实 window 环境中执行 JS 代码,基于 crypto.randomUUID() 双向通信并异步返回可序列化的结果,内置 10秒超时熔断防护:
async getHostState() {
try {
const data = await this._executor.execute(`
(() => {
// 直接访问网页挂载在 window 上的全局变量
return window.__INITIAL_STATE__ ? window.__INITIAL_STATE__.user : null;
})()
`);
this._logger.log("获取宿主数据成功:", data);
} catch (err) {
this._logger.error("页面执行失败或超时:", err.message);
}
}
_sanitizer)专为对抗严格的 CSP (Content Security Policy) 与 Trusted Types 策略而设计:
// 1. 安全设置 innerHTML
this._sanitizer.setInnerHTML(targetElement, "<div class='card'>Content</div>");
// 2. 创建 TrustedHTML 对象
const trustedHtml = this._sanitizer.createTrustedHTML("<span>Text</span>");
// 3. 安全向页面注入 Script
this._sanitizer.injectScript(this._hostDocument, "console.log('Script Injected')");
// 4. 安全向页面注入 Style 样式 (自动追加应用专属命名空间)
this._sanitizer.injectStyle(this._hostDocument, ".my-btn { color: red; }", "custom-btn-style");
_module)当模块遇到需要完全推倒重建的配置变更时,可调用 requestReset() 触发无竞态热重置。框架会自动 await onDisable()、防抖拉取最新配置并即时执行 onEnable():
async onConfigChange(key, newVal, oldVal) {
if (key === "engineType") {
// 核心引擎改变,请求完整生命周期热重启
await this._module.requestReset();
}
}
在现代单页应用 (React/Vue) 中,DOM 经常被框架的 Virtual DOM 重新渲染抹除。uiGuard 能利用 requestIdleCallback 在低开销空闲帧中自动监控并毫秒级自愈恢复 UI 组件。
class UIComponentModule extends Caliber.Module {
id = "ui-enhancer";
name = "界面增强器";
// 声明 UI 守护契约
uiGuard = {
target: ".main-header", // UI 组件应挂载的目标父节点选择器
component: ".my-custom-badge" // 组件自身的 CSS 选择器(用于检测是否存在)
};
// 当 target 存在但 component 被删除/丢失时,框架自动触发 onRender 重新恢复
onRender(targetElement) {
const badge = this._hostDocument.createElement("span");
badge.className = "my-custom-badge";
badge.textContent = "VIP 标志";
targetElement.appendChild(badge);
}
// 模块停用或清理时触发
onCleanup() {
this._hostDocument.querySelector(this.uiGuard.component)?.remove();
}
}
match 属性支持强大的内置分词引擎,自动解析路径与 Query 参数:
// 1. 动态 RESTful 命名参数 (自动提取到 context.params)
match = "/user/:userId/post/:postId";
// 2. 可选命名参数
match = "/list/:category?";
// 3. 全局通配符路径 (提取为 context.params._)
match = "/docs/*";
// 4. 正则表达式匹配
match = /^\/goods\/(?<goodsId>\d+)\.html$/;
// 5. 规则组合 (数组)
match = ["/dashboard", "/settings", "/*"];
框架自动通过全局协调中心监听 pushState、replaceState 与 popstate:
await onDisable();await onEnable(context);await onNavigate(context)。当 createApp 设置 isDebug: true 时,框架会自动激活 ModuleAuditor。
在模块调用 onDisable() 结束并完全完成异步卸载后,审计员会自动比对追踪表,精准标红报告资源泄漏:
(Target, Type, Listener, useCapture) 四元组精准追踪;setInterval;_scheduler.register。❌ [Auditor] LEAK DETECTED in module 'myFeature': Event listener(s) for type(s) [click] were NOT removed from element: <button id="btn">
❌ [Auditor] LEAK DETECTED in module 'myFeature': A setInterval (ID: 15) was NOT cleared.
❌ [Auditor] LEAK DETECTED in module 'myFeature': A scheduler task (ID: Symbol(.avatar)) was NOT unregistered.
当需要动态卸载整个脚本时,可以调用 kernel.destroy() 实现零内存残留的无痕卸载:
// 获取内核实例 (挂载在 hostWindow 上)
const kernelKey = Object.keys(window).find(k => k.startsWith("CALIBER_INSTANCE_"));
const kernel = window[kernelKey];
// 触发异步全量注销
await kernel.destroy();
await onDisable() 卸载所有已激活模块;MutationObserver 进入零功耗休眠模式;delete 内核单例键。以下是一个集成了多行文本域配置、DOM 调度器分帧处理、网络请求拦截、UI 守护自愈与异步生命周期的综合实用模块:
class VideoDownloadEnhancer extends Caliber.Module {
id = "video-enhancer";
name = "高清视频下载助手";
description = "自动捕获视频直链,添加快捷下载按钮与多行关键词过滤";
match = "/video/:videoId";
defaultConfig = {
enabled: true,
filterKeywords: {
type: "textarea",
value: "广告\n赞助商\n推广",
label: "屏蔽过滤词",
description: "包含这些关键词的视频将自动跳过",
inputProps: { rows: 3, placeholder: "每行一个关键词..." },
divider: "bottom"
},
btnColor: {
type: "color",
value: "#007aff",
label: "下载按钮主题色"
}
};
uiGuard = {
target: ".video-toolbar",
component: ".custom-download-btn"
};
#videoRealUrl = null;
#schedulerTaskId = null;
// 1. 模块激活 (支持 async)
async onEnable(context) {
this._logger.log(`当前视频 ID: ${context.params.videoId}`);
// (1) 注册网络请求拦截器,捕获接口返回的真实直链
this._interceptor
.target({
url: window.location.origin + "/api/video/detail",
method: "GET"
})
.onResponse((data) => {
if (data?.downloadUrl) {
this.#videoRealUrl = data.downloadUrl;
this._logger.log("已成功解析到视频直链:", this.#videoRealUrl);
}
})
.register(this.id);
// (2) 使用 DOM 调度器分帧给视频标题着色
this.#schedulerTaskId = this._scheduler.register(".video-title", (el) => {
el.style.borderLeft = `4px solid ${this._config.btnColor}`;
}, { processExisting: true });
}
// 2. 声明式 UI 守护与自愈
onRender(targetElement) {
const btn = this._hostDocument.createElement("button");
btn.className = "custom-download-btn";
btn.textContent = "📥 极速下载";
btn.style.cssText = `
padding: 6px 14px; color: white; border: none; border-radius: 4px;
cursor: pointer; background-color: ${this._config.btnColor};
`;
btn.onclick = () => {
if (this.#videoRealUrl) {
window.open(this.#videoRealUrl, "_blank");
} else {
alert("直链解析中,请稍候点击...");
}
};
targetElement.appendChild(btn);
}
onCleanup() {
this._hostDocument.querySelector(this.uiGuard.component)?.remove();
}
// 3. 配置实时双向同步
onConfigChange(key, newValue) {
if (key === "btnColor") {
const btn = this._hostDocument.querySelector(this.uiGuard.component);
if (btn) btn.style.backgroundColor = newValue;
}
}
// 4. 模块异步优雅卸载
async onDisable() {
this._interceptor.removeHook(this.id);
if (this.#schedulerTaskId) {
this._scheduler.unregister(this.#schedulerTaskId);
this.#schedulerTaskId = null;
}
this.onCleanup();
this.#videoRealUrl = null;
this._logger.log("模块已完全卸载释放。");
}
}
// 启动框架
(async () => {
await Caliber.createApp({
appName: "VideoHelperApp",
isDebug: true,
modules: [VideoDownloadEnhancer],
settingsPanel: {
bottom: 60,
right: 0 // 智能贴边平角
}
});
})();