文档参考
注意
- 以下内容均为在特定时间特定环境的个人经验之谈,可能会在以后或不同环境中不适用。
Greasemonkey/Tampermonkey API相关
- 自某个版本开始有了
GM.*
形式的API,可以用于取代旧版本的GM_*
形式的API。GM.*
的API函数有些是异步函数,具体请查看文档,而GM_*
的API函数是同步的。
unsafeWindow
- 在声明了
@grant unsafeWindow
之后,脚本上下文中的window
会被替换成一个由插件提供的”沙箱window”。 - 在沙箱环境下,
Worker
以及一些API可能会无法工作。 - 可以使用
window = unsafeWindow;
的方式脱离沙箱环境,但要保证这行在需要脱离沙箱的代码之前。另外也可以保留一个原来的window
的引用以备用。
ECMAScript 相关
Worker 和 DedicatedWorkerGlobalScope
Worker
可以通过如下方法来实现从代码直接加载而不用URL以实现跨域加载Worker
1
2
3
4const content = 'Worker中要执行的代码';
const url = URL.createObjectURL(new Blob([content], { type: 'text/javascript' }));
const worker = new Worker(url);
// ...还有一种可行的从跨域URL加载的方式,简单说就是在上述方法中使用
importScripts
加载启用CORS的域的代码
1 |
|
SharedWorker 和 SharedWorkerGlobalScope
SharedWorker
在通过上述方式使用时没有效果(但这种方法不会提示错误,是否真的不可用还有待研究),由于同源策略也无法执行你自己的脚本。目前还没有找到一个合理有效的方法在用户脚本中使用SharedWorker
- 参考How can I load a shared web worker with a user-script?
- 注:上述页面中的部分答案可能有用,但这不应该是
SharedWorker
本身应用的场景
ServiceWorker 和 ServiceWorkerGlobalScope
ServiceWorker
同SharedWorker
没有合理有效的方法在用户脚本中使用,但使用上述方法时均直接提示错误,因此目前这些方法对ServiceWorker
均不可用
模块化
从URL中加载的方式 import / export
从某个版本开始,各大浏览器实现了动态
import
,因此可以通过这种方式在用户脚本中从URL加载模块(Module)示例
1
2
3
4
5
6
7
8
9
10// 模块中的代码
export function f () { console.log('here is function f'); }
export const k = 1;
export default function func () { console.log('here is function func'); }
// 用户脚本或其他地方的代码,注意动态import的使用方式和静态import的使用方式有所不同
const mod = await import(URL);
console.log(mod.f, mod.f());
console.log(mod.k);
console.log(mod.default); // 注意这里,default指代的是用export default导出的东西在
Web Worker
中暂时无法使用import
,参考Web Workers - How To Import Modules- Chrome 80 修复了这个bug
直接执行代码的方式 eval / Function / AsyncFunction
- 如果想要异步加载,需要保证加载过程的代码中的每个异步函数都有正确地
await
,否则可能导致加载顺序不一致 - 关于
eval
和Function
性能问题:在 Chrome 中测试得到eval
有最高的效率;在 Firefox 中测试得到Function
更快。此外,在 Chrome 中的执行速度比 Firefox 整体快了数倍
网页请求相关
CORS
在用户脚本中跨域获取HTTP请求自己的文件可以通过脚本的
@resource
实现,或把文件挂在代码托管的网站上,因为这些网站通常是默认开启CORS的如果需要请求他人的文件(如调用API等)可以使用GM.xmlHttpRequest
CORS对
Worker
/SharedWorker
/ServiceWorker
都不能产生效果,因此你不能通过如下方式从跨域URL加载1
2
3var blockedWorker = new Worker("https://not-example.com/");
blockedWorker = new SharedWorker("https://not-example.com/");
navigator.serviceWorker.register('https://not-example.com/sw.js');参考Why can I fetch this Cross origin file but I can’t create a Worker from it?
importScripts
受CORS控制,因此可以用这个方式解决上述的问题,见上文
Content-Security-Policy
- 如果网站启用CSP并且作出限制,用户脚本基本上没法做被限制的操作,不过我目前没有遇到
GM.xmlHttpRequest / XMLHttpRequest / Fetch_API 对比
特性 | GM.xmlHttpRequest |
XMLHttpRequest |
Fetch_API |
---|---|---|---|
跨域请求 | 油猴控制 | CORS控制 | CORS控制 |
修改User-Agent |
有效 | 无效 | 无效 |
修改Host |
有效 | 无效 | 无效 |
修改Origin |
有效 | 无效 | 无效 |
修改Referer |
有效 | 无效 | 受限 |
修改Cookie |
有效 | 无效 | 无效 |
修改Sec- |
有效 | 无效 | 无效 |
HTTP 2 |
不支持 | 支持 | 支持 |
此外在使用
GM.xmlHttpRequest
的时候要自行填写所有需要的 Header,这可能会带来麻烦和其他风险表中是否有效的判断是指是否能通过给定的API来打到对应的目的
Fetch_API
中的 受限 是指可以修改为同页面下的任意URL- 示例
1
2
3
4
5
6
7// 假定在 http://example.com/demo 下进行的请求
fetch('http://example.com/demo/api', {
method: 'GET',
referrerPolicy: 'unsafe-url',
referrer: 'http://cross-origin.com/balabala' // 在这种情况下,设置的 referrer 无效,最终的 Referrer 不变
referrer: 'ThisIsReferrer' // 在这种情况下,最终的 Referrer 为 http://example.com/demo/ThisIsReferrer
});测试环境为 Chrome 80 下的默认设置,TamperMonkey 4.9