✅ 完全支持的
- 所有标准的 UserScript 元数据(@name, @match, @grant 等)
- 基本的 JavaScript 功能(DOM操作、事件处理、AJAX等)
- GM_API(油猴提供的特殊API)
⚠️ 部分支持的
- 某些特定的 @grant 权限需要明确声明
- 外部库引入(@require)可能受网络限制
- 跨域请求需要相应权限
❌ 不支持的
- 某些浏览器特定API(如Chrome扩展API)
- 系统级操作(文件系统访问等)
- 不安全的操作(绕过安全限制)
完整的 UserScript 元数据支持列表
js
复制代码
// ==UserScript==
// @name 脚本名称 ✅
// @namespace 命名空间 ✅
// @version 版本号 ✅
// @author 作者 ✅
// @description 描述 ✅
// @homepage 主页 ✅
// @homepageURL 主页URL ✅
// @website 网站 ✅
// @source 源码地址 ✅
// @icon 图标 ✅
// @iconURL 图标URL ✅
// @defaulticon 默认图标 ✅
// 运行时机 ✅
// @run-at document-start
// @run-at document-body
// @run-at document-end
// @run-at document-idle
// @run-at context-menu
// 匹配规则 ✅
// @match URL匹配 ✅
// @include 包含URL ✅
// @exclude 排除URL ✅
// 资源引入 ✅
// @require 引入JS库 ✅
// @resource 引入资源 ✅
// @css 引入CSS ✅
// 权限控制 ✅
// @grant API权限 ✅
// @noframes 不在iframe运行 ✅
// @unwrap 不包裹函数 ✅
// 更新相关 ✅
// @updateURL 更新地址 ✅
// @downloadURL 下载地址 ✅
// @supportURL 支持地址 ✅
// ==/UserScript==
实际使用示例
基础功能脚本
js
复制代码
// ==UserScript==
// @name 全能网页助手
// @namespace https://github.com/my-scripts
// @version 2.1.0
// @description 提供多种网页增强功能
// @author ScriptMaster
// @match *://*/*
// @run-at document-end
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @grant GM_notification
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @resource customCSS https://example.com/style.css
// @updateURL https://example.com/myscript.meta.js
// @downloadURL https://example.com/myscript.user.js
// @supportURL https://example.com/support
// @icon https://www.google.com/favicon.ico
// ==/UserScript==
(function() {
'use strict';
// 你的脚本代码...
})();
高级功能展示
js
复制代码
// ==UserScript==
// @name 高级网页工具
// @namespace http://tampermonkey.net/
// @version 1.5
// @description 展示油猴的高级功能支持
// @author You
// @match https://*/*
// @exclude https://admin.*/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_listValues
// @grant GM_addValueChangeListener
// @grant GM_removeValueChangeListener
// @grant GM_setClipboard
// @grant GM_xmlhttpRequest
// @grant GM_download
// @grant GM_getTab
// @grant GM_saveTab
// @grant GM_getTabs
// @grant GM_notification
// @grant GM_openInTab
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_getResourceText
// @grant GM_getResourceURL
// @grant GM_addStyle
// @grant GM_log
// @grant unsafeWindow
// @require https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.10.7/dayjs.min.js
// @resource importantData https://example.com/data.json
// @css body { font-family: Arial, sans-serif; }
// ==/UserScript==
(function() {
'use strict';
// 1. 数据存储
GM_setValue('user_preferences', {
theme: 'dark',
language: 'zh-CN',
auto_save: true
});
const prefs = GM_getValue('user_preferences', {});
console.log('用户设置:', prefs);
// 2. 网络请求
GM_xmlhttpRequest({
method: 'GET',
url: 'https://api.example.com/data',
onload: function(response) {
console.log('API响应:', response.responseText);
}
});
// 3. 下载文件
GM_download({
url: 'https://example.com/file.pdf',
name: 'document.pdf',
onload: function() {
console.log('下载完成');
}
});
// 4. 通知提醒
GM_notification({
text: '脚本已加载',
title: '提示',
timeout: 3000
});
// 5. 注册菜单命令
GM_registerMenuCommand('执行功能', function() {
alert('菜单命令被点击!');
});
// 6. 添加样式
GM_addStyle(`
.my-custom-class {
border: 2px solid blue !important;
background: yellow !important;
}
`);
// 7. 监听数据变化
GM_addValueChangeListener('settings', function(name, oldValue, newValue) {
console.log(`设置从 ${oldValue} 变为 ${newValue}`);
});
})();
基础网络请求案例
GET 请求示例
js
复制代码
// ==UserScript==
// @name 网络请求示例
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 演示各种网络请求
// @author You
// @match https://httpbin.org/*
// @grant GM_xmlhttpRequest
// ==/UserScript==
(function() {
'use strict';
// 基础GET请求
function simpleGetRequest() {
GM_xmlhttpRequest({
method: "GET",
url: "https://httpbin.org/get",
onload: function(response) {
console.log("GET请求成功:", JSON.parse(response.responseText));
showNotification('GET请求成功!');
},
onerror: function(error) {
console.error("GET请求失败:", error);
showNotification('请求失败', 'error');
}
});
}
// 带参数的GET请求
function getWithParams() {
const params = new URLSearchParams({
page: 1,
limit: 20,
keyword: 'test'
});
GM_xmlhttpRequest({
method: "GET",
url: `https://httpbin.org/get?${params}`,
onload: function(response) {
const data = JSON.parse(response.responseText);
console.log("带参数GET:", data.args);
}
});
}
// 添加按钮测试
addTestButtons();
function addTestButtons() {
const container = document.createElement('div');
container.innerHTML = `
<div style="position:fixed; top:10px; right:10px; background:white; padding:10px; border:1px solid #ccc; z-index:10000;">
<h4>请求测试</h4>
<button id="test-get">测试GET</button>
<button id="test-post">测试POST</button>
<button id="test-json">测试JSON</button>
<button id="test-upload">测试上传</button>
</div>
`;
document.body.appendChild(container);
document.getElementById('test-get').addEventListener('click', simpleGetRequest);
document.getElementById('test-post').addEventListener('click', postRequest);
document.getElementById('test-json').addEventListener('click', jsonRequest);
document.getElementById('test-upload').addEventListener('click', uploadFile);
}
})();
POST 请求示例
js
复制代码
// POST表单数据
function postRequest() {
const formData = new URLSearchParams();
formData.append('username', 'testuser');
formData.append('password', 'testpass');
formData.append('email', 'test@example.com');
GM_xmlhttpRequest({
method: "POST",
url: "https://httpbin.org/post",
data: formData.toString(),
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
onload: function(response) {
const result = JSON.parse(response.responseText);
console.log("POST表单数据:", result.form);
showNotification('表单提交成功!');
}
});
}
// JSON请求
function jsonRequest() {
const jsonData = {
title: "测试文章",
content: "这是通过油猴脚本发送的内容",
tags: ["技术", "脚本"],
published: true
};
GM_xmlhttpRequest({
method: "POST",
url: "https://httpbin.org/post",
data: JSON.stringify(jsonData),
headers: {
"Content-Type": "application/json"
},
onload: function(response) {
const result = JSON.parse(response.responseText);
console.log("JSON请求结果:", result.json);
showNotification('JSON数据发送成功!');
}
});
}
文件上传案例
基础文件上传
js
复制代码
// ==UserScript==
// @name 文件上传示例
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 演示文件上传功能
// @author You
// @match https://httpbin.org/*
// @grant GM_xmlhttpRequest
// ==/UserScript==
(function() {
'use strict';
// 创建文件上传界面
function createUploadInterface() {
const uploadArea = document.createElement('div');
uploadArea.innerHTML = `
<div style="position:fixed; top:100px; right:10px; background:white; padding:15px; border:2px dashed #ccc; z-index:10000; width:300px;">
<h3>文件上传测试</h3>
<input type="file" id="fileInput" multiple style="margin:10px 0;">
<button id="uploadBtn">上传文件</button>
<div id="progress" style="margin-top:10px;"></div>
<div id="uploadResult" style="margin-top:10px;"></div>
</div>
`;
document.body.appendChild(uploadArea);
document.getElementById('uploadBtn').addEventListener('click', handleFileUpload);
}
// 处理文件上传
function handleFileUpload() {
const fileInput = document.getElementById('fileInput');
const files = fileInput.files;
if (files.length === 0) {
showNotification('请选择文件', 'error');
return;
}
for (let file of files) {
uploadSingleFile(file);
}
}
// 上传单个文件
function uploadSingleFile(file) {
const formData = new FormData();
formData.append('file', file);
formData.append('description', '通过油猴脚本上传');
formData.append('timestamp', new Date().toISOString());
updateProgress(`开始上传: ${file.name}`);
GM_xmlhttpRequest({
method: "POST",
url: "https://httpbin.org/post",
data: formData,
// FormData会自动设置Content-Type和boundary
onload: function(response) {
const result = JSON.parse(response.responseText);
console.log(`文件 ${file.name} 上传成功:`, result);
updateProgress(`✓ ${file.name} 上传完成`);
showUploadResult(file.name, 'success');
},
onerror: function(error) {
console.error(`文件 ${file.name} 上传失败:`, error);
updateProgress(`✗ ${file.name} 上传失败`);
showUploadResult(file.name, 'error');
},
onprogress: function(event) {
if (event.lengthComputable) {
const percent = Math.round((event.loaded / event.total) * 100);
updateProgress(`上传中: ${file.name} - ${percent}%`);
}
}
});
}
function updateProgress(message) {
const progress = document.getElementById('progress');
progress.innerHTML = `<div style="font-size:12px; color:#666;">${message}</div>`;
}
function showUploadResult(filename, status) {
const resultDiv = document.getElementById('uploadResult');
const color = status === 'success' ? 'green' : 'red';
resultDiv.innerHTML += `<div style="color:${color}; font-size:12px;">${filename} - ${status === 'success' ? '成功' : '失败'}</div>`;
}
createUploadInterface();
})();
图片上传和预览案例
js
复制代码
// ==UserScript==
// @name 图片上传和预览
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 图片上传、预览和压缩
// @author You
// @match https://*/*
// @grant GM_xmlhttpRequest
// ==/UserScript==
(function() {
'use strict';
function createImageUploader() {
const uploader = document.createElement('div');
uploader.innerHTML = `
<div style="position:fixed; bottom:20px; left:20px; background:white; padding:15px; border:1px solid #ccc; z-index:10000; width:350px; max-height:500px; overflow-y:auto;">
<h4>图片上传工具</h4>
<input type="file" id="imageInput" accept="image/*" multiple>
<div style="margin:10px 0;">
<label>压缩质量: </label>
<input type="range" id="quality" min="0.1" max="1" step="0.1" value="0.8">
<span id="qualityValue">80%</span>
</div>
<button id="compressAndUpload">压缩并上传</button>
<div id="imagePreview" style="margin-top:10px;"></div>
<div id="uploadStatus"></div>
</div>
`;
document.body.appendChild(uploader);
// 预览图片
document.getElementById('imageInput').addEventListener('change', previewImages);
document.getElementById('quality').addEventListener('input', updateQualityValue);
document.getElementById('compressAndUpload').addEventListener('click', compressAndUploadImages);
}
function previewImages(event) {
const preview = document.getElementById('imagePreview');
preview.innerHTML = '';
const files = event.target.files;
for (let file of files) {
if (!file.type.startsWith('image/')) continue;
const reader = new FileReader();
reader.onload = function(e) {
const imgContainer = document.createElement('div');
imgContainer.style.cssText = 'margin:5px; border:1px solid #ddd; padding:5px;';
imgContainer.innerHTML = `
<img src="${e.target.result}" style="max-width:100px; max-height:100px;">
<div style="font-size:10px;">${file.name}</div>
<div style="font-size:10px;">${(file.size/1024).toFixed(1)}KB</div>
`;
preview.appendChild(imgContainer);
};
reader.readAsDataURL(file);
}
}
function updateQualityValue() {
const quality = document.getElementById('quality').value;
document.getElementById('qualityValue').textContent = Math.round(quality * 100) + '%';
}
// 图片压缩和上传
function compressAndUploadImages() {
const files = document.getElementById('imageInput').files;
const quality = parseFloat(document.getElementById('quality').value);
if (files.length === 0) {
showNotification('请选择图片', 'error');
return;
}
for (let file of files) {
if (file.type.startsWith('image/')) {
compressImage(file, quality).then(compressedFile => {
uploadCompressedImage(compressedFile, file.name);
});
}
}
}
function compressImage(file, quality) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = function(e) {
const img = new Image();
img.onload = function() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 计算压缩后的尺寸(最大宽度800px)
let width = img.width;
let height = img.height;
const maxWidth = 800;
if (width > maxWidth) {
height = (maxWidth / width) * height;
width = maxWidth;
}
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob(function(blob) {
const compressedFile = new File([blob], file.name, {
type: 'image/jpeg',
lastModified: new Date().getTime()
});
resolve(compressedFile);
}, 'image/jpeg', quality);
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
});
}
function uploadCompressedImage(file, originalName) {
const formData = new FormData();
formData.append('image', file);
formData.append('original_name', originalName);
formData.append('compressed_size', file.size);
updateUploadStatus(`上传压缩图片: ${originalName}`);
GM_xmlhttpRequest({
method: "POST",
url: "https://httpbin.org/post",
data: formData,
onload: function(response) {
const result = JSON.parse(response.responseText);
const savedKB = (result.files.image.size - file.size) / 1024;
updateUploadStatus(`✓ ${originalName} 上传成功 (节省: ${savedKB.toFixed(1)}KB)`, 'success');
},
onerror: function(error) {
updateUploadStatus(`✗ ${originalName} 上传失败`, 'error');
}
});
}
function updateUploadStatus(message, type = 'info') {
const statusDiv = document.getElementById('uploadStatus');
const color = type === 'success' ? 'green' : type === 'error' ? 'red' : 'blue';
statusDiv.innerHTML += `<div style="color:${color}; font-size:12px; margin:2px 0;">${message}</div>`;
statusDiv.scrollTop = statusDiv.scrollHeight;
}
createImageUploader();
})();
特殊功能支持
跨域请求
js
复制代码
// 需要 @grant GM_xmlhttpRequest
GM_xmlhttpRequest({
method: "GET",
url: "https://api.github.com/users/octocat",
onload: function(response) {
const data = JSON.parse(response.responseText);
console.log('GitHub用户:', data);
},
onerror: function(error) {
console.error('请求失败:', error);
}
});
剪贴板操作
js
复制代码
// 需要 @grant GM_setClipboard
function copyToClipboard(text) {
GM_setClipboard(text, 'text').then(() => {
console.log('已复制到剪贴板');
});
}
标签页管理
js
复制代码
// 需要 @grant GM_getTabs, GM_saveTab
GM_getTabs(function(tabs) {
console.log('所有标签页:', tabs);
});
兼容性说明
不同浏览器的差异
- Tampermonkey(Chrome/Edge):功能最完整
- Greasemonkey(Firefox):大部分功能支持
- Violentmonkey:轻量级替代品
常见限制
- 内容安全策略(CSP) :某些网站可能阻止脚本执行
- 跨域限制:需要明确使用GM_xmlhttpRequest
- API权限:某些GM_* API需要用户授权