#静态网页配合油猴脚本实现跨域请求
如果想做一个纯静态网页,而又想跨域请求,可以通过油猴脚本来实现
#油猴脚本
// ==UserScript==
// @name 注入 GM_xmlhttpRequest 和 GM.xmlHttpRequest
// @namespace https://docs.scriptcat.org/
// @version 0.1.0
// @description try to take over the world!
// @author You
// @match http://localhost:5173/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=localhost
// @connect *
// @grant GM_xmlhttpRequest
// @grant GM.xmlHttpRequest
// @grant unsafeWindow
// ==/UserScript==
(function () {
'use strict';
// 向 window 注入 GM_xmlhttpRequest
// 将 GM_xmlhttpRequest 函数赋值给 unsafeWindow 的一个新属性
// 这样页面的 <script> 标签就能通过 window.GM_xmlhttpRequest 访问到它
if (typeof unsafeWindow.GM_xmlhttpRequest === 'undefined') {
unsafeWindow.GM_xmlhttpRequest = GM_xmlhttpRequest;
}
// 向 window 注入 GM.xmlHttpRequest
// 1. 检查页面上是否存在 GM 对象,如果不存在,则创建一个
if (typeof unsafeWindow.GM === 'undefined') {
// 2. 将油猴沙箱中的 GM.xmlHttpRequest 函数赋值给页面 window 上的 GM.xmlHttpRequest
// 由于 GM.xmlHttpRequest 本身就返回 Promise,所以页面可以直接 await 它
unsafeWindow.GM = { xmlHttpRequest: GM.xmlHttpRequest };
}
})();注意将 // @match http://localhost:5173/* 替换成你的静态网页地址
这样,在你的静态网页中,你可以通过 GM_xmlhttpRequest 或者 GM.xmlHttpRequest 来发起跨域请求。例如:
GM_xmlhttpRequest({
method: "GET",
url: "https://example.com/",
headers: {
"Content-Type": "application/json"
},
onload: function(response) {
console.log(response.responseText);
}
});
const r = await GM.xmlHttpRequest({ url: "https://example.com/" }).catch(e => console.error(e));
console.log(r.responseText);
try {
// 如果在GM.xmlHttpRequest 中指定 responseType: 'json',
// 那么会自动将响应数据解析为 JSON 对象
const gmResponse = await GM.xmlHttpRequest({
url: 'https://jsonplaceholder.typicode.com/todos/1',
responseType: 'json'
})
// object
console.log(typeof gmResponse.response)
} catch (error) {
console.error('请求失败:', error)
}
try {
const gmResponse = await GM.xmlHttpRequest({
url: 'https://jsonplaceholder.typicode.com/todos/1'
})
// string
console.log(typeof gmResponse.response)
} catch (error) {
console.error('请求失败:', error)
}#添加类型声明文件
如果你在使用 TypeScript,你需要为 GM_xmlhttpRequest 和 GM.xmlHttpRequest 添加类型声明。
在项目的 src 目录下创建一个文件,例如 GM_xmlhttpRequest.d.ts 或者 globals.d.ts 或者 tampermonkey.d.ts,并添加以下内容:
// To ensure this file is treated as a module.
export {};
/**
* Represents the response object received from a GM_xmlhttpRequest.
* @template TContext The type of the context object passed in the request.
* @template TResponse The expected type of the `response` property if `responseType` is 'json'.
*/
interface GM_Response<TResponse = any, TContext = any> {
finalUrl: string;
readyState: 0 | 1 | 2 | 3 | 4;
status: number;
statusText: string;
responseHeaders: string;
/** The response data. If `responseType` was 'json', this will be of type `TResponse`. */
response: TResponse;
responseXML: Document | null;
responseText: string;
context?: TContext;
}
/**
* The base details for an XML HTTP request.
* @template TContext The type of the context object to be passed.
*/
interface GM_Request<TContext = any> {
method?: 'GET' | 'POST' | 'HEAD' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS';
url: string | URL | File | Blob;
headers?: Record<string, string>;
data?: string | Blob | File | object | any[] | FormData | URLSearchParams;
redirect?: 'follow' | 'error' | 'manual';
cookie?: string;
cookiePartition?: object;
topLevelSite?: string;
binary?: boolean;
nocache?: boolean;
revalidate?: boolean;
timeout?: number;
context?: TContext;
/**
* The expected response type. Set to 'json' to have Tampermonkey automatically parse the response.
*/
responseType?: 'arraybuffer' | 'blob' | 'json' | 'stream';
overrideMimeType?: string;
anonymous?: boolean;
fetch?: boolean;
user?: string;
password?: string;
}
/**
* Details for a callback-based GM_xmlhttpRequest.
* @template TResponse The expected type of the `response` property if `responseType` is 'json'.
* @template TContext The type of the context object to be passed.
*/
interface GM_CallbackRequest<TResponse = any, TContext = any> extends GM_Request<TContext> {
onabort?: (response: GM_Response<TResponse, TContext>) => void;
onerror?: (response: GM_Response<TResponse, TContext>) => void;
onloadstart?: (response: GM_Response<TResponse, TContext>) => void;
onprogress?: (response: GM_Response<TResponse, TContext>) => void;
onreadystatechange?: (response: GM_Response<TResponse, TContext>) => void;
ontimeout?: (response: GM_Response<TResponse, TContext>) => void;
onload: (response: GM_Response<TResponse, TContext>) => void;
}
interface GM_AbortHandle {
abort: () => void;
}
declare global {
function GM_xmlhttpRequest<TResponse = any, TContext = any>(
details: GM_CallbackRequest<TResponse, TContext>
): GM_AbortHandle;
const GM: {
xmlHttpRequest<TResponse = any, TContext = any>(
details: GM_Request<TContext>
): Promise<GM_Response<TResponse, TContext>> & GM_AbortHandle;
};
}这样项目中使用GM_xmlhttpRequest 和 GM.xmlHttpRequest 就会得到类型提示。
这样会导致
.d.ts文件中 ESLint 报错:ESLint: Unexpected any. Specify a different type. (@typescript-eslint/no-explicit-any)可以把
any改成never,这样.d.ts文件中 ESLint 不报错,但这样在调用处会报错Property [property name] does not exist on type never。还可以把
any改成unknown,这样在调用处会要求显式指定类型。

