油猴脚本开发详解

这里我使用的是vite-plugin-monkey, 一个 vite 插件, 用于开发油猴脚本

关于油猴脚本的开发, 请参考油猴脚本开发

框架详解

主要信息在vite-plugin-monkey 中文文档中有详细介绍

使用

创建项目

pnpm create monkey
# npm create monkey
# yarn create monkey

从以下模板选择

JavaScriptTypeScript
empty (only js)empty-ts (only ts)
vanilla (js + css)vanilla-ts (ts + css)
vuevue-ts
reactreact-ts
preactpreact-ts
sveltesvelte-ts
solidsolid-ts

这里我使用的是empty-ts模板

项目完成后,最主要的就是修改vite.config.ts文件,完成对项目的配置
这是一个简单的例子

import { defineConfig } from "vite";
import monkey, { util } from "vite-plugin-monkey";
import AutoImport from "unplugin-auto-import/vite";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    AutoImport({
      imports: [util.unimportPreset],
    }),
    monkey({
      entry: "src/main.ts",
      userscript: {
        icon: "https://vitejs.dev/logo.svg",
        author: "MOL",
        namespace: "npm/vite-plugin-monkey",
        description: "A Sctipt for Grailed",
        match: ["https://www.grailed.com/sell/*"],
      },
    }),
  ],
});

Userscript Header 就是在油猴脚本开头注释的代码,以下是对这些配置参数的描述,他们全部都可以在官方文档上找到

如果想要添加 Userscript Header 的更多信息,可以在vite.config.ts中添加userscript字段

下面是来自官方文档的一些配置参数

Userscript Header 配置参数

下面是一个简单的例子

import { defineConfig } from "vite";
import monkey, { util } from "vite-plugin-monkey";
import AutoImport from "unplugin-auto-import/vite";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    AutoImport({
      imports: [util.unimportPreset],
    }),
    monkey({
      entry: "src/main.ts",
      userscript: {
        icon: "https://vitejs.dev/logo.svg",
        author: "MOL",
        namespace: "npm/vite-plugin-monkey",
        description: "A Sctipt for Grailed",
        match: ["https://www.grailed.com/sell/*"],
      },
    }),
  ],
});

@name:脚本名称
@namespace:脚本的命名空间
@version:脚本的版本,用于检查更新。但需要用户设置更新频率
@author:脚本的作者
@description:脚本简短重要的描述
@homepage、@homepageURL、@website and @source:在选项页面上用于将脚本名称链接到给定页面的作者主页。 请注意,如果@namespace 标记以“ http://”开头,其内容也将用于此
@icon、@iconURL、@defaulticon:低分率的脚本图标,会在脚本管理列表上显示
@icon64、@icon64URL:此脚本图标为 64x64 像素。如果还配置了@icon,那么@icon 图像将在选项页面被缩放
@updateURL:更新脚本的地址,注意:只有存在 @version 标签才会去更新
@downloadURL:定义检测到更新时将从中下载脚本的 URL。如果值为 none,则不会执行更新检查
@supportURL:定义用户可以用来报告问题并获得个人支持的 URL
@include:脚本应该运行的页面, 可以使用正则匹配。 允许多个标签, 请注意 @include 不支持 url hash 参数。用法如下:

// @include http://www.tampermonkey.net/*
// @include http://*
// @include https://*
// @include /^https://www.tampermonkey.net/.*$/ // @include *`

这样在
@match@include 标签含义类似
@exclude 排除 URL,即使它们包含在 @include 或 @match 中。同样允许多个标签。
@require 指向一个脚本文件,会在脚本运行前加载并执行 我们可以使用这个配置引入 jQuery 不过要注意:通过 @require 加载的脚本及其“use strict”语句可能会影响用户脚本的 strict 模式!

// @require https://code.jquery.com/jquery-2.1.4.min.js
// @require https://code.jquery.com/jquery-2.1.3.min.js#sha256=23456...
// @require https://code.jquery.com/jquery-2.1.2.min.js#md5=34567...,sha256=6789...
// @require tampermonkey://vendor/jquery.js
// @require tampermonkey://vendor/jszip/jszip.js`

@resource:预加载一些资源,HTML、JSON,脚本可以通过 gm_getresourceurl 和 gm_getresourcetext 访问资源。

// @resource icon1 http://www.tampermonkey.net/favicon.ico
// @resource icon2 /images/icon.png
// @resource html http://www.tampermonkey.net/index.html
// @resource xml http://www.tampermonkey.net/crx/tampermonkey.xml
// @resource SRIsecured1 http://www.tampermonkey.net/favicon.ico#md5=123434...
// @resource SRIsecured2 http://www.tampermonkey.net/favicon.ico#md5=123434...;sha256=234234...

@connect:此标记定义了允许由 GM_xmlhttpRequest 检索的域(没有顶级域),包括子域。

// @connect <value>

value 可以是以下值:

*   域名可以是类似:tampermokey.net(这种可以允许子域名)
*   子域名可以类似:safari.tampermokey.net
*   self:可以将脚本当前运行的域列入白名单
*   localhost:访问本机地址
*   IP地址,类似:1.2.3.4

@run-at:定义脚本被注入的时刻。与其他脚本处理程序相反,@run-at 定义了脚本想要运行的第一个可能时刻。这意味着可能会发生这样的情况,使用@require 标记的脚本可能会在文档已经加载之后执行,因为获取所需的脚本需要很长时间。无论如何,在给定的注入时刻之后发生的所有 DOMNodeInserted 和 DOMContentLoaded 事件都会被缓存,并在注入脚本时交付给脚本。
脚本会被尽可能快地注入

// @run-at document-start

如果 Body 元素存在,脚本将被注入

// @run-at document-body

脚本将会在 DOMContentLoaded 事件调度之后注入,如果不写 @run-at 那么就按照这种方式注入

// @run-at document-end

如果在浏览器上下文菜单中单击该脚本,则将注入该脚本(仅适用于基于 Chrome 的桌面浏览器)

// @run-at context-menu

@grant
@grant 被用于设置 GM** 类型函数的白名单,也就是允许哪些 GM*_ 类型的函数在油猴脚本中使用。 GM__ 函数是一些 unsafeWindow 对象和一些功能强大的 window 函数,如果没有 @grant 标签,Tampermonkey 无法调用 GM** 函数。例如下面常见的 GM** 类型的函数:

// @grant GM_setValue
// @grant GM_getValue
// @grant GM_setClipboard
// @grant unsafeWindow
// @grant window.close
// @grant window.focus
// @grant window.onurlchange

@noframes:这个标签表明脚本在主页面上运行,而不是在 iframes 框里。

应用编程接口

为了实现更多深度扩展网站,整合数据的需求,油猴还对外开发了更高层次的 API。这些 API 可以使你直接访问页面函数和变量、直接添加样式、存储数据(不跨域)、设置监听事件、使用 XHR 和打开新的浏览器 Tab 页等等。

unsafeWindow

unsafeWindow 对象提供访问页面的 Javascript 函数和变量的权限。此对象不用使用 @grant 获取权限。

GM_addStyle(css)

GM_addStyle 函数可以向 DOM 中直接添加 CSS 样式,参数是字符串样式。

// @grant    GM_addStyle

GM_addStyle(`html body{background-image: url(https://img-blog.csdnimg.cn/20191109172245482.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9maXp6ei5ibG9nLmNzZG4ubmV0,size_16,color_FFFFFF,t_70)`)
数据存储问题

在 TM 编写脚本时,有时会遇到临时存储数据的问题,TM 提供了一种方案:

*   GM_setValue(key,value) 向 storage 中存储一个键值对,键为key,值为value
*   GM_getValue(key) 从 storage 中获取 key 的值
*   GM_listValues() 列出 storage 所有的值
*   GM_deleteValue(key) 从 storage 中删除 key 的值
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_listValues

GM_setValue('time1', '2019年11月10日11:43:28')
GM_setValue('time2', '2019年11月10日11:43:41')
console.log('获取time',GM_getValue('time')
console.log('storage中所有的值',GM_listValues())
GM_deleteValue('time1')
console.log('获取time1',GM_getValue('time1')
console.log('storage中所有的值',GM_listValues())
GM_openInTab(url, options), GM_openInTab(url, loadInBackground)

在新标签页中打开一个网站,options 参数可以有下面的值:

*  active:判断是否聚焦到新标签页
*  insert:在当前标签后面添加新标签
*  setParent:在标签关闭后重新聚焦当前标签
*  incognito:在隐私窗口下打开标签
*  loadInBackground:该参数与 active 参数的意义相反
// @grant GM_openInTab

GM_openInTab('https://www.baidu.com/', {active: true, insert: true, setParent:true})
GM_addValueChangeListener(name, function(name, old_value, new_value, remote) {})

在 Storage 里添加一个改变事件的监听,并返回监听 id。name 是被观察的变量。回调函数中的 remote 变量是显示此值是从另一个标签页的实例修改的(true)还是在此脚本实例中修改的(false)。因此,不同浏览器标签页下的脚本可以使用此功能相互通信。可以使用此 API 实现不同浏览器 Tab 的相互通讯,此函数返回一个 listener_id 用于移除监听事件。

GM_removeValueChangeListener(listener_id)

通过 listener_id 移除监听事件。

GM_log(message)

向终端打印日志

GM_getResourceText(name)

获取脚本开头中定义的@resource 中的内容

GM_getResourceURL(name)

获取脚本头处预定义的@resource 标记的 base64 编码 URI。

GM_registerMenuCommand(name, fn, accessKey)

注册一个菜单,该菜单将显示在运行此脚本的页面的 Tampermonkey 菜单上,并返回菜单命令 ID。

GM_unregisterMenuCommand(menuCmdId)

用给定的菜单命令 ID 注销先前由 GM_registerMenuCommand 注册的菜单命令。

GM_xmlhttpRequest(details)

发送 xmlHttpRequest 请求,details 有下面这些参数:

  • method 请求方法:GET, HEAD, POST
    url 请求网址
    headers 请求头
    data POST 需要发送的内容
    cookie 请求所需的 Cookie
    binary 发送的二进制内容
    nocache 不缓存资源
    revalidate 重新验证可能缓存的内容
    timeout 超时时间 毫秒
    context 将添加到响应对象的属性
    responseType 响应类型:arraybuffer, blob, json
    overrideMimeType MIME 类型的请求
    anonymous 匿名发送 不发送 Cookie
    fetch (beta) 使用 fetch 方法 而不是会用 xhr request
    username 用于身份验证的用户名
    password 用于身份验证的密码
    onabort 请求中止时要执行的回调
    onerror 如果请求以错误结束则执行的回调
    onloadstart 请求开始加载时要执行的回调
    onprogress 请求取得一定进展时要执行的回调
    onreadystatechange 请求就绪状态更改时要执行的回调
    ontimeout 请求超时失败时要执行的回调
    onload 如果请求已加载,将执行的回调。
    该回调有一个参数,并且有下面的属性:
GM_xmlhttpRequest 返回具有以下方法的对象:

abort 取消该请求的函数
注意:details 参数不支持同步请求

GMdownload(details)、GMdownload(url,name)

下载资源到本地磁盘。details 的属性:

url:资源的 url
name:文件名,出于安全原因,文件的扩展名必须在 TM 参数页面的的白名单里
headers:如 GM_xmlhttpRequest 一样设置请求头部
saveAs:boolean 值,显示一个保存的弹窗
onerror:下载以失败结束执行的回调函数
onload 现在完成后执行的回调函数
onprogress 下载过程中变化的回调函数
ontimeout 下载超时执行的回调函数

GMnotification(details,ondone)、GMnotification(text,title,image,onclick)

显示一个 H5 的桌面通知,或者高亮当前 tab。details 的属性:

text:通知的问题 如果高亮就 就不需要
title:通知的标题
image:图片
highlight:一个 boolean 标志,是否高亮 tab
silent - 一个 boolean 是否播放音乐
timeout:通知显示的时间 0 表示 一直显示
ondone:通知被关闭时 无论是被点击还是超时 执行的函数
onclick:点击通知触发的函数
所有参数的作用与其对应的详细信息属性挂件完全相同。

GM_setClipboard(data,info)

复制数据到粘贴板,参数 info 可以是对象如 {type: 'text', mimetype: 'text/plain'},或者是一个字符串 text、html。