GM_* API 速成:GM_setValue、GM_xmlhttpRequest、GM_addStyle
元数据头里的 @grant 申请来的「特权」,具体就是一组以 GM_ 开头的函数。它们解决页面脚本的三大痛点:数据存不住、跨域请求发不出、样式注入太啰嗦。这一篇讲清最核心的几个 API,其余照猫画虎即可。
GM_setValue / GM_getValue:脚本自己的存储
页面里的 localStorage 会被清浏览记录清掉,还和站点共享。GM 存储则跟着脚本走,跨站点可用、不怕清理:
// ==UserScript==
// @name 存储演示
// @match https://example.com/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// ==/UserScript==
GM_setValue("visited", 42);
const n = GM_getValue("visited", 0); // 第二个参数是默认值
GM_deleteValue("visited");
// GM_listValues() 返回所有键名数组,适合遍历清理
for (const key of GM_listValues()) {
console.log(key, GM_getValue(key));
}
典型用途:记录「已读过的帖子 ID」、保存用户的界面偏好、缓存接口返回。和扩展的 chrome.storage.local 对应关系几乎一一对应,学会这边,迁移到扩展时零成本。
GM_xmlhttpRequest:绕过同源策略的跨域请求
这是用户脚本最锋利的一把刀。页面的 fetch 受同源策略限制,GM_xmlhttpRequest 则由管理器代理发起,不受页面同源策略约束(当然仍受目标服务器与 CSP 的现实约束):
// ==UserScript==
// @name 跨域请求演示
// @match https://example.com/*
// @grant GM_xmlhttpRequest
// @connect api.github.com
// ==/UserScript==
GM_xmlhttpRequest({
method: "GET",
url: "https://api.github.com/repos/tampermonkey/tampermonkey",
headers: { Accept: "application/vnd.github+json" },
onload(res) {
const data = JSON.parse(res.responseText);
console.log("star 数:", data.stargazers_count);
},
onerror() {
console.error("请求失败");
}
});
三个要点。第一,@connect 声明允许请求的域名,管理器遇到未声明的域名会弹窗询问用户,把 @connect 写全可以避免运行中被弹窗打断。第二,回调风格是 onload / onerror / ontimeout,不是 Promise;要异步组合就自己包一层 new Promise。第三,能力越大责任越大:这个 API 能把页面数据发往任意服务器,也是恶意脚本最爱用的通道——再次提醒,装别人的脚本先看它往哪里发请求。
GM_addStyle:一行注入样式
不用 createElement 也不用手动 append,一行搞定:
// @grant GM_addStyle
GM_addStyle(`
.ad-banner, .promo-layer { display: none !important; }
body { letter-spacing: 0.02em; }
`);
配合 @run-at document-start 使用,样式在页面渲染前就位,用户看不到闪变。更精细的时机控制可以直接操作 DOM,但对绝大多数「改外观」需求,GM_addStyle 就够了。
其他常用成员
| API | 用途 |
|---|---|
| GM_registerMenuCommand | 在油猴面板加自定义菜单项,点击触发回调 |
| GM_setClipboard | 写入系统剪贴板 |
| GM_notification | 弹出系统通知 |
| GM_getResourceText / URL | 取 @resource 引入的资源 |
| GM_addValueChangeListener | 监听存储变化,跨标签页联动 |
GM_registerMenuCommand 值得单独记一笔,它让脚本有了「设置面板」:注册一个「切换暗黑模式」菜单项,点击后翻转存储里的布尔值,比改代码切换友好得多。
组合实战:记状态的访问计数器
// ==UserScript==
// @name 站点访问计数
// @match https://example.com/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// ==/UserScript==
const count = GM_getValue("count", 0) + 1;
GM_setValue("count", count);
console.log("这是你第", count, "次访问");
GM_registerMenuCommand("清零计数", () => {
GM_setValue("count", 0);
location.reload();
});
存储、菜单、刷新联动,二十行覆盖三个 API。把这 twenty 行变成自己的模板,之后写任何脚本都从它开始。文本统计与模式匹配做多了会自然想强化正则功底,正则匹配写法可参考本站《正则表达式从入门到精通》:正则实战。
需要说明:GM API 是 Tampermonkey 的实现约定,个别函数签名可能随版本微调,以 Tampermonkey 文档为准;与扩展 chrome API 的对照迁移,到第三章后你会更有体感。
回调转 Promise 的通用封装
GM API 偏回调风格,现代代码习惯 async/await。一个几行的封装函数就能统一风格,值得背下来:
function gmr(options) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
...options,
onload: (res) => (res.status >= 200 && res.status < 300
? resolve(res)
: reject(new Error(`HTTP ${res.status}`))),
onerror: () => reject(new Error("network error")),
ontimeout: () => reject(new Error("timeout"))
});
});
}
// 用起来就是标准 async 风格
async function main() {
try {
const res = await gmr({ method: "GET", url: "https://api.example.com/data" });
console.log(JSON.parse(res.responseText));
} catch (e) {
console.warn("请求失败:", e.message);
}
}
封装层还能顺带做重试、超时控制、JSON 解析。脚本写大之后,你会有一个自己的 gmr.js 小工具库,它是脚本工程化的第一块砖。
权限申请的最小化对照
把本篇 API 与 @grant 的对应关系列成表,写脚本时对照勾选,避免「全部 grant 一遍」的坏习惯:
| 需求 | 需要的 grant |
|---|---|
| 记住用户偏好 | GM_setValue / GM_getValue |
| 调第三方接口 | GM_xmlhttpRequest + @connect 域名 |
| 改页面外观 | GM_addStyle(或不加,直接操作 DOM) |
| 加菜单开关 | GM_registerMenuCommand |
| 读写剪贴板 | GM_setClipboard |
| 读取 @resource 资源 | GM_getResourceText / GM_getResourceURL |
一个常见误区:用了 GM_addStyle 却 grant 全家桶。多申请不会报错,但会让脚本审计变难——Greasy Fork 的审核也会提示过宽的权限。逐项对应,是脚本作者的基本素养。
迁移视角:GM 与 chrome 的对照
第三章会正式讲扩展 API,这里先给一张速查表,让你知道「搬家时每样东西去哪了」:
| GM API | 扩展对应物 |
|---|---|
| GM_setValue / GM_getValue | chrome.storage.local.set / get |
| GM_xmlhttpRequest | 后台上下文的 fetch(配 host_permissions) |
| GM_addStyle | content_scripts 的 css 数组或 scripting.insertCSS |
| GM_registerMenuCommand | contextMenus 或 action 弹窗 |
| GM_notification | chrome.notifications.create |
对照着看你会发现:脚本只是把这些能力「包装」得更顺手,底层模型与扩展一致。所以本篇学的不只是油猴,是两类工具共通的能力模型。等读到第四章,这张表会让迁移零成本。
需要说明:GM API 是 Tampermonkey 的实现约定,个别函数签名可能随版本微调,以 Tampermonkey 文档为准。
GM API 的函数名与参数拼写没有编译器兜底,写错即 undefined,编辑器的语法检查(如 HBuilderX 效率技巧 里介绍的工程化配置)能提前拦一道。
本章由 WorkBuddy 原创整理,API 行为依据 Chrome 扩展官方文档(Manifest V3)。
