油猴脚本支持的所有 UserScript

✅ 完全支持的

  • 所有标准的 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:轻量级替代品

常见限制

  1. 内容安全策略(CSP) :某些网站可能阻止脚本执行
  2. 跨域限制:需要明确使用GM_xmlhttpRequest
  3. API权限:某些GM_* API需要用户授权
相关推荐
披萨心肠6 小时前
理解JavaScript中的函数参数传递
前端·javascript
吞吞07116 小时前
Alpine.js 技术文档
前端
彭于晏爱编程6 小时前
Vite 打包超 500KB 警告优化实录
前端
飞翔的佩奇6 小时前
【完整源码+数据集+部署教程】【天线&运输】直升机战机类型识别目标检测系统源码&数据集全套:改进yolo11-CSP-EDLAN
前端·python·yolo·计算机视觉·数据集·yolo11·直升机战机类型识别目标检测系统
JA+6 小时前
vue 实时数据表格组件 (stk-table-vue)
前端·javascript·vue.js
那年窗外下的雪.7 小时前
鸿蒙ArkUI布局与样式进阶(十二)——自定义TabBar + class类机制全解析(含手机商城底部导航案例)
开发语言·前端·javascript·华为·智能手机·harmonyos·arkui
IT_陈寒7 小时前
Python性能优化:5个被低估但效果惊人的内置函数实战解析
前端·人工智能·后端
00后程序员张7 小时前
Fiddler使用教程,全面掌握Fiddler抓包工具的配置方法、代理设置与调试技巧(HTTPHTTPS全解析)
前端·测试工具·ios·小程序·fiddler·uni-app·webview