油猴脚本学习1——元数据头部

元数据脚本基本结构就是

基本的脚本就是这样的

// ==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. 管理脚本的更新流程

当用户点击更新时,油猴会:

  1. 根据版本号判断有更新。

  2. 显示一个对比界面,让你查看新版本修改了哪些代码和元数据。

  3. 允许你选择"立即安装"或"忽略此次更新"。

版本号是这个流程的唯一凭证

3. 向用户传达变更的严重性

油猴社区通常遵循 语义化版本 规范,用户可以通过版本号的变化快速了解这次更新的性质:

版本号变化 含义 用户应有的预期
主版本号 变化 (例如 2.5.0 -> 3.0.0 重大更新 脚本可能进行了彻底重写,可能与网站新布局适配,但可能与你用的其他脚本冲突 ,或设置项被重置。需要谨慎更新。
次版本号 变化 (例如 2.5.0 -> 2.6.0 新增功能 加入了新功能,但不会破坏现有功能。通常是安全且值得更新的。
修订号 变化 (例如 2.5.0 -> 2.5.1 问题修复 修复了一些Bug,没有新功能。强烈建议更新
4. 脚本仓库的强制要求

GreasyForkOpenUserJS 等主流的油猴脚本发布平台上:

  • @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.bodynull

    • 可以拦截和修改最初的 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-end DOM 解析完成 DOM 就绪 进行中 很高
document-idle 页面完全加载 完全就绪 已完成 最高(默认)
context-menu 用户触发 任意时机 任意状态 特殊用途

实际选择建议

  1. 需要拦截修改原始内容document-start

  2. 操作页面主要元素document-end(最常用)

  3. 依赖完整页面资源document-idle(默认)

  4. 工具类、按需使用context-menu

  5. 特定布局调整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+
相关推荐
半夏知半秋4 小时前
redis-哨兵模式配置整理
数据库·redis·笔记·后端·学习·lua·安全架构
帅帅dl4 小时前
Git学习(2)
学习
勇往直前plus4 小时前
学习和掌握RabbitMQ及其与springboot的整合实践(篇一)
spring boot·学习·spring cloud·rabbitmq·java-rabbitmq
华清远见成都中心4 小时前
嵌入式需要掌握哪些核心技能?
物联网·学习·嵌入式·华清远见
codeyanwu4 小时前
Excel 学习笔记
学习·excel·1024程序员节
宁清明6 小时前
【小宁的学习日记2 C语言】C语言判断
c语言·学习·算法
又是忙碌的一天10 小时前
算法学习 13
数据结构·学习·算法
微露清风13 小时前
系统性学习C++-第八讲-vector类
java·c++·学习
月临水13 小时前
Git 学习笔记
笔记·git·学习·1024程序员节