10分钟了解如何开发专属自己的浏览器插件

2,770 阅读5分钟

小知识,大挑战!本文正在参与「程序员必备小知识」创作活动

本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。

环境准备

油猴插件下载站点,该站点最大的优点就是不用翻墙、不用关注公众号,可以直接下载各种常用的插件,比如 ADBlock Plus,FE-Helper,vue-devtools 等等

当然,坏消息是该网站吧 f12 给干了,我不是那样的人,防啥呢😒

插件安装

插件管理页面

image.png

image.png

然后吧下载的插件扔进去,点击安装确认就行

新增脚本

image.png

image.png

脚本分析

基于新建的脚本,可以发现,开头用行注释声明的注释内容,会被油猴解析为相关的参数

我们在 // ==UserScript==// ==/UserScript== 中间,添加行注释,后面接上需要的注解以及属性值,这些数据用于定义脚本的信息,这些数据笼统的称为元数据块

我们需要编写的业务逻辑,建议放在行注释 // ==/UserScript== 后边

元数据块详解

官方文档(英)

有的注解会需要多个值,我们可以另起一行来赋新值,示例如下:

// @include http://www.example.com/a
// @include http://www.example.com/b

支持赋予多个值的注解有如下这些:

@exclude
@grant
@include
@match
@require
@resource

@description

简短的描述这个脚本可以实现哪些功能

@include

现在提供了类似功能的注解 @match,但会更加安全(指对通配符 * 的处理更加严格)

@match

这个脚本可以匹配哪些 url,指在哪些 url 下会自动运行;如果不指定,将不会运行在任何站点下

// @include http://www.example.com/*
// @include http://*
// @include *

@exclude

指在 @include@match 中匹配的 url 中,需要排除掉哪些不需求匹配的 url

@grant

油猴提供了一些高级 API,这些方法不可以直接在脚本里使用,必须使用 @grant 声明才能使用对应的方法

// @grant    GM.getValue
// @grant    GM.setValue

@icon

指定一个图片的 url,用于在脚本管理列表页面显示的图标,尺寸建议 32x32

@name

脚本的名称

@namespace

脚本的命名空间,建议用自己的域名或自己的git域名,再加上项目名来做区别,需要确保独一无二

@noframes

当出现时,该命令将限制脚本的执行。该脚本将只在顶级文档中运行,而不会在嵌套框架中运行。它不需要参数,要么存在,要么不存在。默认情况下,允许脚本在框架中运行。

@require

帮助我们引入额外的在线的 js 文件,比如我最喜欢的 jq,操作 dom 一把梭哈

当我们需要引入的多个 js 文件全部加载完成后,才会运行后面的函数主体

@resource

引入外部的资源,并指定唯一的别名,后续使用 GM.getResourceUrl(别名) 直接使用定义的资源

// @resource resourceName http://www.example.com/example.png

@run-at

定义脚本的运行时机

document-end

默认使用此值,当文档主体加载完成,但其他资源(css,js,imgage等)还未就绪时,执行脚本

document-start

脚本将在任何文档开始加载之前运行,因此在任何脚本运行或图像加载之前运行。

document-idle

脚本将在页面和所有资源(图像、样式表等)加载并运行页面脚本之后运行。

@version

定义脚本的版本信息

高级 API

GM 对象中,除了 GM.info() 之外,其他的如果需要使用,必须通过 @grant 进行授权

GM.info

返回当前脚本的详细数据

let info = GM.info();

GM.setValue

允许用户脚本作者跨页面加载和起源持久保存简单值,值类型仅限于数字、布尔、字符串

GM.setValue('key', 'value');

GM.getValue

此方法检索一个用 GM.setValue 设置的值。会返回一个 Promise 对象

let val = await GM.getValue('key');

GM.deleteValue

此方法从存储中删除现有的名称/值对。返回一个被标记为 resolve 或 reject 的 Promise 对象,但不会有值

let result = await GM.deleteValue('key');  // null

GM.listValues

此方法检索此脚本已存储的首选项名称数组。返回一个 Promise 对象,解析得到一个字符串的数组

这个方法或者变更为 GM.listKeys() 更为合适

let keys = await GM.listValues();

GM.getResourceUrl

使用 @resource 定义的图片资源+变量名,可以在函数中通过此方法选择变量名来引用

// ==UserScript==
// @name Image resource example
// @resource logo ../icons/laptop.png
// @grant GM.getResourceUrl
// ==/UserScript==

(async function() {
let img = document.createElement("img");
img.src = await GM.getResourceUrl("logo");
document.body.appendChild(img);
})();

GM.notification

使用基础浏览器和操作系统的通知机制向用户显示通知

function GM.notification(text, title, image, onclick) {}
// or 
function GM.notification({
    'text': 'str',
    'title': 'str',
    'image': 'image url',
    onclick: function() {},
    ondone: function() {}
}){}

GM.openInTab

新开一个标签页

GM.openInTab("http://www.example.com/");

GM.registerMenuCommand

允许用户脚本向用户脚本命令菜单添加一个项。

比如,在脚本中编写 GM.registerMenuCommand('测试标题', ()=>console.log('123'))

image.png

/
 * @param {String} caption      要显示在菜单项上的标题
 * @param {function} commandFunc    当用户选择此菜单项时要调用的函数
 * @param {String} accessKey    当菜单打开时,可用于选择命令的单个字符。应该是标题中的一个字母。
 */
function GM.registerMenuCommand(caption, commandFunc, accessKey) {}

GM.setClipboard

设置操作系统剪贴板的当前内容。最常规的做法,用于处理在一些站点上复制内容时,多出的小尾巴

GM.setClipboard('memo');

GM.xmlHttpRequest

这个方法执行与标准的XMLHttpRequest对象类似的功能,仅允许这些请求跨越相同的来源策略边界。

传递的参数有点偏多,具体请参考 文档

或者考虑使用 fetch 替代?

unsafeWindow

这是脚本中的内置实例,等价于浏览器访问的顶层页面的 window 对象


这么详细的开发文档,还不来写2个脚本练练手吗?要是实在不知道咋开头,直接 down 我这个 自动签到 的脚本下来改改就是你自己的好东西~

创作不易,期待大家的鼓励❤~

image.png