TamperMonkey

各类实用的 Chrome 扩展是不少人选择 Chrome 浏览器的重要原因,经过多年发展,Chrome 的扩展种类已经非常丰富,除了那些「即装即用」的小工具之外,也有很多被誉为「神器」的强大扩展程序。

「油猴」也可以通过安装各类脚本对网站进行定制。不过它能定制的不仅仅是网站的样式,还能实现更多更强大的功能,例如:

  • 直接下载百度网盘文件
  • 重新定制繁杂的微博页面
  • 去掉视频播放广告
  • 将网站默认的「二维码登录」改回「账号密码登录」
  • 绕过搜索引擎的跳转提示
  • 还原清新的小说阅读模式
  • 豆瓣和 IMDb 互相显示评分

TamperMonkey简介

TamperMonkey可以让我们编写一些js脚本来帮助我们做一些特定的工作,它的脚本的编写与普通的js代码无异,但是还是有一些自己的特性。

全部的特性可以直接去看官方文档:https://www.tampermonkey.net/documentation.php?ext=dhdg

这里就简单介绍几个常用的:

常用属性

属性名作用
name油猴脚本的名字
namespace命名空间,类似于Java的包名,用来区分相同名称的脚本,一般写成作者名字或者网址就可以了
version脚本版本,油猴脚本的更新会读取这个版本号
description描述,用来告诉用户这个脚本是干什么用的
author作者名字
match只有匹配的网址才会执行对应的脚本,例如*http://*http://www.baidu.com/*等,参见谷歌开发者文档
grant指定脚本运行所需权限,如果脚本拥有相应的权限,就可以调用油猴扩展提供的API与浏览器进行交互。如果设置为none的话,则不使用沙箱环境,脚本会直接运行在网页的环境中,这时候无法使用大部分油猴扩展的API。如果不指定的话,油猴会默认添加几个最常用的API
require如果脚本依赖其他js库的话,可以使用require指令,在运行脚本之前先加载其他库,常见用法是加载jquery
connect当用户使用GM_xmlhttpRequest请求远程数据的时候,需要使用connect指定允许访问的域名,支持域名、子域名、IP地址以及*通配符
updateURL脚本更新网址,当油猴扩展检查更新的时候,会尝试从这个网址下载脚本,然后比对版本号确认是否更新

举例:时间掌控者

tamperMonkey上有一款非常火的脚本叫做时间掌控者,可以以任意倍速播放视频,我们就简单分析一下它的工作原理

首先是一些头部的特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// @name        计时器掌控者|视频广告跳过|视频广告加速器
// @name:en TimerHooker
// @name:zh-CN 计时器掌控者|视频广告跳过|视频广告加速器
// @namespace https://gitee.com/HGJing/everthing-hook/
// @version 1.0.39
// @description 控制网页计时器速度|加速跳过页面计时广告|视频快进(慢放)|跳过广告|支持几乎所有网页.
// @description:en it can hook the timer speed to change.
// @description:zh-CN 控制网页计时器速度|加速跳过页面计时广告|跳过广告|支持几乎所有网页.
// @include *
// @require https://greasyfork.org/scripts/372672-everything-hook/code/Everything-Hook.js?version=784972
// @author Cangshi
// @match http://*/*
// @run-at document-start
// @grant none
// @license GPL-3.0-or-later

其中最主要的就是require特性,它引入了一个叫做everything-hook的插件,可以劫持所有浏览器的方法

这个组件中大部分代码都是用来在页面中插入几个按钮,或者注册一些快捷键的,另外就是用everything-hook劫持setTimeout和setInterval方法。

但是其中最重要,最后发挥作用的就是

1
2
3
4
5
6
7
8
9
10
11
changeVideoSpeed: function () {
var rate = 1 / this._percentage;
rate > 16 && (rate = 16);
rate < 0.065 && (rate = 0.065);
var videos = querySelectorAll(document, 'video', true) || [];
if (videos.length) {
for (var i = 0; i < videos.length; i++) {
videos[i].playbackRate = rate;
}
}
}

获取页面中所有的video标签,改变其playbackRate,来修改其播放速率。

实践

既然了解了如何编写一个油猴脚本,我们就简单尝试一下。

作为一个程序员,我们平时开发时肯定会同时在不同环境开发测试不同的平台,这些大量的url我们一般都是找个地方存起来,或者存在本地,或者存在浏览器,那我们是不是可以写个插件将所有url配置起来,每次需要的时候把插件打开,然后根据配置名去直接打开。

直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// ==UserScript==
// @name URL Manager
// @namespace https://sunra.top/
// @version 0.1
// @description Manage URL!
// @author You
// @match http://*/*
// @grant none
// @require https://res.cloudinary.com/dvtfhjxi4/raw/upload/v1590135992/url_rpyrxm.js
// ==/UserScript==

(function() {
'use strict';


document.onkeydown = function (event) {
var e = event || window.event;
if (e.key == 'q' && e.ctrlKey) {
if (document.getElementById("gsbn_urls")) {
return;
}
addEnvironmentFormToBody();
}
}

function generateForm() {
return `<form id="gsbn_urls">
<label>Platform:</label>
<select id="openPlatform">
${Object.keys(urls).map((platform) => {
return `<option value="${platform}">${platform}</option>`
}).join('\n')}
</select>
<label>Environment:</label>
<select id="openUrl">
</select>
<button id="openBtn">open</button>
</form>`;
}

function addEnvironmentFormToBody() {
var form = document.createElement('div');
form.style = "position:absolute;width:600px;height:100px;top:200px;"
form.innerHTML = generateForm();
document.body.appendChild(form);
addChangeEventListenerToPlatformSelect();
addClickEventListenerToOpenButton();
}

function addClickEventListenerToOpenButton () {
document.getElementById('openBtn').onclick = function () {
var urlSelect = document.getElementById("openUrl");
var index = urlSelect.selectedIndex;
window.open(urlSelect.options[index].value);
}
}

function addChangeEventListenerToPlatformSelect() {
var platformSelect = document.getElementById('openPlatform');
var urlSelect = document.getElementById("openUrl");
platformSelect.onchange = function () {
var index = platformSelect.selectedIndex;
urlSelect.innerHTML = "";
var urlOptions = urls[platformSelect.options[index].value];
urlSelect.innerHTML = Object.keys(urlOptions).map((env) => {
return `<option value='${urlOptions[env]}'>${env}</option>`;
}).join('\n');
}
fireChange(platformSelect);
}

function fireChange(element) {
if ("createEvent" in document) {
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", false, true);
element.dispatchEvent(evt);
}
else {
element.fireEvent("onchange");
}
}
})();

上面的代码中url是通过require属性从cdn中导入了脚本中定义的,大致格式就是这样的。

1
2
3
4
5
6
7
8
9
10
var urls = {
pm_console: {
dev2: "",
dev: ""
},
hm_console: {
dev2: "",
prod: ""
}
}

最终效果就是,在任意页面输入快捷键Ctrl + Q就会出现下面这个简陋的界面,可以在左边选择了

一些小坑

  • 每次修改脚本如果想要立即生效,请重启浏览器。
  • fireEvent在谷歌浏览器中不好使,需要用dispatchEvent。
  • 需要在脚本的设置页面设置哪些网站不使用本脚本,或者哪些使用,符合规则的脚本才会生效。

还有大量优秀的插件等我们去发现,最大的插件共享网站:https://greasyfork.org/zh-CN