元数据脚本基本结构就是
基本的脚本就是这样的
// ==UserScript==
// @name 我的第一个脚本
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 修改网页背景色
// @author You
// @match https://*/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
document.body.style.backgroundColor = '#f0f8ff';
})();
// ==UserScript==
// @key value
// @key value
// ==/UserScript==
然后中间填写
// @name My Awesome Script
// @name 百度搜索优化,
名字空间,为脚本命名,命名主要是为了避免相互冲突
想象一下这个场景:
你安装了两个脚本,脚本A 和脚本B。
脚本A 定义了一个全局变量
var config = { ... }。脚本B 也定义了一个同名的全局变量
var config = { ... }。当两个脚本都在同一个页面上运行时,后加载的脚本会覆盖先加载的脚本的变量。这会导致脚本A无法正常工作 ,因为它的
config被篡改了。命名空间就是为了解决这个问题而生的。
2. 工作原理
当你在脚本的元数据(
@namespace)中声明一个命名空间后,油猴会将你的脚本包裹在一个"沙箱"或"隔离环境"中 。你脚本中声明的所有变量和函数,默认都不会泄露到全局window对象上。但是,如果你必须要设置一个全局变量(例如某个网站的特殊API要求),命名空间就为这个全局变量提供了一个"前缀"或"所属范围",极大地降低了冲突的概率。
// @version 1.0
// @version 2.1.5
// @version 0.8.0-beta
版本号
油猴脚本中版本号的核心作用
1. 触发更新检测 - 最核心的功能
这是版本号在油猴中存在的首要理由。
-
油猴管理器会定期(例如每天)检查你安装的脚本是否有更新。
-
检查的方式就是对比脚本元数据中的
@version和脚本发布网址上的@version。 -
如果发布网址上的版本号高于你本地安装的版本号,油猴就会在管理面板中显示一个更新按钮(通常带有数字徽标),提示你有新版本可用。
-
没有版本号,这个自动更新机制就完全失效了。
2. 管理脚本的更新流程
当用户点击更新时,油猴会:
-
根据版本号判断有更新。
-
显示一个对比界面,让你查看新版本修改了哪些代码和元数据。
-
允许你选择"立即安装"或"忽略此次更新"。
版本号是这个流程的唯一凭证。
3. 向用户传达变更的严重性
油猴社区通常遵循 语义化版本 规范,用户可以通过版本号的变化快速了解这次更新的性质:
| 版本号变化 | 含义 | 用户应有的预期 |
|---|---|---|
主版本号 变化 (例如 2.5.0 -> 3.0.0) |
重大更新 | 脚本可能进行了彻底重写,可能与网站新布局适配,但可能与你用的其他脚本冲突 ,或设置项被重置。需要谨慎更新。 |
次版本号 变化 (例如 2.5.0 -> 2.6.0) |
新增功能 | 加入了新功能,但不会破坏现有功能。通常是安全且值得更新的。 |
修订号 变化 (例如 2.5.0 -> 2.5.1) |
问题修复 | 修复了一些Bug,没有新功能。强烈建议更新。 |
4. 脚本仓库的强制要求
在 GreasyFork 、OpenUserJS 等主流的油猴脚本发布平台上:
-
@version是必须的元数据,没有它你无法成功发布脚本。 -
每次更新脚本时,都必须提高版本号,平台不允许你上传一个版本号与现有版本相同或更低的脚本。
描述
// @description 增强网页功能
// @description 自动填充表单,移除广告
作者
// @author Your Name
// @author Anonymous
3. 匹配规则字段
// @match *://*.example.com/*
// @match https://www.google.com/search\*
// @match http://localhost:8080/app/\*
@include - 宽松匹配
javascript
// @include http://*
// @include https://*
// @include *://*.example.com/path/*
@exclude - 排除URL
javascript
// @exclude *://*.example.com/admin/*
// @exclude https://www.google.com/search?q=test*
运行时
// @run-at document-start // 尽早执行
// @run-at document-body // body元素出现时
// @run-at document-end // DOMContentLoaded时
// @run-at document-idle // 默认,页面加载完成后
// @run-at context-menu // 右键菜单点击时
1.
// @run-at document-start执行时机:资源开始加载,但尚未解析 DOM。
特点:
脚本在页面 HTML 刚开始下载时就注入
document.readyState === "loading"DOM 树尚未构建,
document.body为null可以拦截和修改最初的 HTML 内容
// ==UserScript==
// @run-at document-start
// ==/UserScript==
// 拦截和修改原始 HTML
document.addEventListener('beforescriptexecute', function(e) {
// 阻止某些脚本执行
if (e.target.src.includes('advertisement')) {
e.preventDefault();
}
});
// 修改尚未解析的页面内容
document.documentElement.addEventListener('DOMNodeInserted', function(e) {
// 监控 DOM 节点的插入
});
2.
// @run-at document-body执行时机 :
<body>元素首次出现时。
特点:
当
document.body不为null但可能为空时执行DOM 刚开始构建,页面内容可能还不完整
比
document-start稍晚,比document-end早典型用途:
// ==UserScript==
// @run-at document-body
// ==/UserScript==// 立即在 body 中添加元素
const loader = document.createElement('div');
loader.id = 'my-custom-loader';
document.body.appendChild(loader);// 设置初始样式或布局
document.body.style.overflow = 'hidden'; // 先隐藏滚动条
3.
// @run-at document-end执行时机 :DOM 解析完成时(
DOMContentLoaded事件)。
特点:
DOM 树已完全构建,可以安全地操作页面元素
图片等资源可能仍在加载
document.readyState === "interactive"这是最常用的时机之一
// ==UserScript==
// @run-at document-end
// ==/UserScript==// 安全地操作页面元素
const buttons = document.querySelectorAll('button');
buttons.forEach(btn => {
btn.style.backgroundColor = 'red';
});// 修改页面内容
const header = document.querySelector('h1');
if (header) {
header.textContent = '已修改的标题';
}
4.
// @run-at document-idle执行时机 :页面完全加载完成后(
window.onload事件)。
特点:
默认值 ,如果不指定
@run-at则使用此选项所有资源(图片、样式表等)都已加载完成
document.readyState === "complete"确保页面完全稳定后再执行脚本
// ==UserScript==
// @run-at document-idle
// ==/UserScript==// 需要页面完全加载后才能执行的操作
const allImages = document.images;
console.log('页面共有图片:', allImages.length);// 进行复杂的 DOM 操作
setTimeout(() => {
// 确保所有动态内容都已加载
processDynamicContent();
}, 1000);5.
// @run-at context-menu执行时机:用户在页面上右键点击脚本名称时。
特点:
脚本不会自动执行
只在用户通过右键菜单选择时才运行
适合工具类、辅助类脚本
// ==UserScript==
// @run-at context-menu
// ==/UserScript==// 右键菜单工具脚本
const selectedText = window.getSelection().toString().trim();if (selectedText) {
// 处理选中的文本
console.log('选中的文本:', selectedText);
// 可以翻译、搜索、格式化等
window.open(https://www.google.com/search?q=${encodeURIComponent(selectedText)});
} else {
alert('请先选择一些文本');
}执行时机对比总结
指令 执行时机 DOM 状态 资源加载 使用频率 document-start最早 未解析 刚开始 中等 document-body较早 body 刚出现 进行中 较少 document-endDOM 解析完成 DOM 就绪 进行中 很高 document-idle页面完全加载 完全就绪 已完成 最高(默认) context-menu用户触发 任意时机 任意状态 特殊用途
实际选择建议
需要拦截修改原始内容 →
document-start操作页面主要元素 →
document-end(最常用)依赖完整页面资源 →
document-idle(默认)工具类、按需使用 →
context-menu特定布局调整 →
document-body
@grant - 权限控制
// @grant none // 不使用特殊API
// @grant GM_setValue // 存储数据
// @grant GM_getValue // 读取数据
// @grant GM_xmlhttpRequest // 跨域请求
// @grant GM_addStyle // 添加CSS
// @grant GM_notification // 桌面通知
// @grant GM_setClipboard // 剪贴板操作
// @grant unsafeWindow // 访问页面window对象
1. // @grant none
含义 :脚本运行在页面的原生环境中,不使用任何油猴特殊 API。
特点:
脚本与页面共享同一个 JavaScript 环境
可以直接访问页面的
window对象权限最低,但执行效率最高
无法使用油猴的存储、网络请求等高级功能
使用场景:
// ==UserScript== // @grant none // ==/UserScript== // 可以直接操作页面变量 const pageVariable = window.somePageVariable; // 可以直接调用页面函数 if (typeof pageFunction === 'function') { pageFunction(); } // 简单的 DOM 操作 document.querySelector('button').click();2.
// @grant GM_setValue和// @grant GM_getValue含义 :启用键值对存储功能,用于持久化保存数据。
特点:
数据在浏览器关闭后依然保存
每个脚本有独立的存储空间
适合保存用户设置、历史记录等
// ==UserScript== // @grant GM_setValue // @grant GM_getValue // ==/UserScript== // 保存用户设置 GM_setValue('theme', 'dark'); GM_setValue('lastVisit', new Date().toISOString()); // 读取设置 const theme = GM_getValue('theme', 'light'); // 第二个参数是默认值 const lastVisit = GM_getValue('lastVisit'); console.log(`当前主题:${theme},最后访问:${lastVisit}`);3.
// @grant GM_xmlhttpRequest含义 :启用跨域网络请求功能。
特点:
可以绕过浏览器的同源策略
支持所有 HTTP 方法(GET、POST 等)
可以自定义请求头、超时时间等
// ==UserScript== // @grant GM_xmlhttpRequest // ==/UserScript== // 调用外部 API GM_xmlhttpRequest({ method: 'GET', url: 'https://api.example.com/data', headers: { 'User-Agent': 'MyUserScript' }, onload: function(response) { const data = JSON.parse(response.responseText); console.log('获取到的数据:', data); }, onerror: function(error) { console.error('请求失败:', error); } });4.
// @grant GM_addStyle含义 :向页面注入 CSS 样式。
特点:
安全地添加样式,避免与页面样式冲突
自动处理样式作用域
适合修改页面外观
// ==UserScript== // @grant GM_addStyle // ==/UserScript== // 添加自定义样式 GM_addStyle(` .my-custom-widget { position: fixed; top: 10px; right: 10px; background: #fff; border: 1px solid #ccc; z-index: 9999; } /* 隐藏广告 */ .advertisement { display: none !important; } /* 修改页面主题 */ body { background-color: #f0f0f0 !important; } `);5.
// @grant GM_notification含义 :显示桌面通知。
特点:
即使用户不在当前标签页也能看到
适合重要事件提醒
使用场景:
// ==UserScript== // @grant GM_notification // ==/UserScript== // 显示通知 GM_notification({ title: '任务完成', text: '数据已成功处理', image: 'https://example.com/icon.png', timeout: 3000, // 3秒后自动关闭 onclick: function() { // 点击通知时的回调 window.focus(); } });6.
// @grant GM_setClipboard含义 :操作系统剪贴板。
特点:
可以安全地复制文本到剪贴板
不需要用户手动复制
使用场景:
// ==UserScript== // @grant GM_setClipboard // ==/UserScript== // 复制文本到剪贴板 function copyToClipboard(text) { GM_setClipboard(text, 'text'); // 第二个参数是数据类型 GM_notification({ title: '复制成功', text: '内容已复制到剪贴板' }); } // 使用示例 copyToClipboard('Hello, World!');7.
// @grant unsafeWindow含义 :直接访问页面的原始 window 对象。
特点:
绕过油猴的安全沙箱
有风险,可能被页面代码检测或干扰
仅在必要时使用
使用场景:
// ==UserScript== // @grant unsafeWindow // ==/UserScript== // 访问页面全局变量 const pageConfig = unsafeWindow.pageConfig; // 修改页面全局变量(谨慎使用!) unsafeWindow.someGlobalFunction = function() { console.log('被脚本修改的函数'); }; // 与页面代码交互 if (unsafeWindow.jQuery) { unsafeWindow.jQuery('body').css('background-color', 'red'); }
Grant 指令 功能 风险等级 使用频率 none无特殊权限 低 高 GM_setValue/GM_getValue数据存储 低 高 GM_xmlhttpRequest跨域请求 中 高 GM_addStyle添加样式 低 高 GM_notification桌面通知 低 中 GM_setClipboard剪贴板操作 中 中 unsafeWindow访问页面对象 高 低
最佳实践建议
按需申请权限:只申请脚本真正需要的权限
优先使用安全API :避免使用
unsafeWindow,除非必要组合使用权限:可以同时申请多个权限
javascript
复制
下载
// ==UserScript== // @grant GM_setValue // @grant GM_getValue // @grant GM_xmlhttpRequest // @grant GM_addStyle // ==/UserScript==测试权限可用性:在使用前检查 API 是否可用
javascript
复制
下载
if (typeof GM_setValue !== 'undefined') { GM_setValue('key', 'value'); }正确使用
@grant指令可以让你的脚本既功能强大又安全可靠。
// ==UserScript==
// @require https://code.jquery.com/jquery-3.6.0.min.js // 先加载
// @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js // 后加载
// @require https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js // 最后加载
// ==/UserScript==
规则 :@require 按声明顺序同步加载和执行。
执行时机
-
在所有
@require加载并执行完成后,才执行脚本主代码 -
执行时机受
@run-at指令影响
实际应用场景
场景 1:使用 UI 库创建界面
// ==UserScript==
// @name 带UI的增强脚本
// @namespace http://tampermonkey.net/
// @version 1.0
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @require https://unpkg.com/sweetalert/dist/sweetalert.min.js
// @match *://example.com/*
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
(function() {
'use strict';
// 使用引入的库
$(document).ready(function() {
// 添加自定义按钮
$('body').prepend('<button id="myBtn">点击我</button>');
$('#myBtn').click(function() {
// 使用 SweetAlert 显示美观的弹窗
swal({
title: "你好!",
text: "这是一个使用外部库的示例",
icon: "success",
buttons: true,
});
});
});
})();
场景 2:工具函数库
javascript
复制
下载
// ==UserScript== // @name 数据处理脚本 // @namespace http://tampermonkey.net/ // @version 1.0 // @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/axios/1.4.0/axios.min.js // @match *://data.example.com/* // @grant none // ==/UserScript== (function() { 'use strict'; // 数据处理 const processData = (data) => { return _.chain(data) .filter(item => item.active) .groupBy('category') .mapValues(group => _.sumBy(group, 'value')) .value(); }; // 使用 axios 进行 HTTP 请求 const fetchData = async () => { try { const response = await axios.get('/api/data'); const processed = processData(response.data); console.log('处理后的数据:', processed); } catch (error) { console.error('请求失败:', error); } }; fetchData(); })();
@resource - 加载资源
javascript
复制
下载
// @resource icon1 http://example.com/icon.png // @resource html http://example.com/template.html // @resource css https://example.com/styles.css
1. 加载图片资源
javascript
// @resource icon1 http://example.com/icon.png
特点:
图片会被缓存,避免重复下载
可以获取图片的 base64 数据 URL
使用示例:
javascript
// ==UserScript==
// @name 使用图片资源的脚本
// @namespace http://tampermonkey.net/
// @version 1.0
// @resource customIcon https://www.google.com/favicon.ico
// @resource banner https://via.placeholder.com/300x100.png
// @match *://*/*
// @grant GM_getResourceURL
// ==/UserScript==
(function() {
'use strict';
// 获取图片的 Data URL
const iconUrl = GM_getResourceURL('customIcon');
const bannerUrl = GM_getResourceURL('banner');
// 在页面中使用图片
const myIcon = document.createElement('img');
myIcon.src = iconUrl;
myIcon.style.width = '16px';
myIcon.style.height = '16px';
document.body.appendChild(myIcon);
})();
2. 加载 HTML 模板
javascript
复制
下载
// @resource html http://example.com/template.html特点:
预加载 HTML 片段或模板
避免在脚本中嵌入大量 HTML 字符串
使用示例:
javascript
复制
下载
// ==UserScript== // @name 使用HTML模板的脚本 // @namespace http://tampermonkey.net/ // @version 1.0 // @resource popupTemplate https://raw.githubusercontent.com/user/repo/master/templates/popup.html // @match *://*/* // @grant GM_getResourceText // ==/UserScript== (function() { 'use strict'; // 获取 HTML 模板内容 const templateHtml = GM_getResourceText('popupTemplate'); // 插入到页面中 const container = document.createElement('div'); container.innerHTML = templateHtml; container.id = 'my-custom-popup'; document.body.appendChild(container); // 现在可以操作模板中的元素 const closeBtn = document.getElementById('close-popup'); if (closeBtn) { closeBtn.addEventListener('click', function() { container.style.display = 'none'; }); } })();
// @resource css https://example.com/styles.css
配套的 GM API 函数
1. GM_getResourceURL(资源名称)
获取资源的 Data URL(适用于图片、字体等二进制资源)。
javascript
mageUrl = GM_getResourceURL('myImage'); // 返回: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
2. GM_getResourceText(资源名称)
获取资源的文本内容(适用于 HTML、CSS、JSON 等文本资源)。
javascript
const template = GM_getResourceText('myTemplate');
// 返回: "<div class='widget'>Hello World</div>"
@connect - 跨域权限
javascript
复制
下载
// @connect example.com
// @connect api.github.com
// @connect localhost
// @connect * // 允许所有域名
6. 更新相关字段
@updateURL - 更新地址
javascript
复制
下载
// @updateURL https://example.com/myscript.meta.js
@downloadURL - 下载地址
javascript
复制
下载
// @downloadURL https://example.com/myscript.user.js
@supportURL - 支持页面
javascript
复制
下载
// @supportURL https://github.com/user/repo/issues
7. 高级功能字段
@noframes - 禁止在iframe运行
javascript
复制
下载
// @noframes
@unwrap - 不包装脚本
javascript
复制
下载
// @unwrap
@nocompat - 兼容性控制
javascript
复制
下载
// @nocompat Chrome 90+
// @nocompat Firefox 88+