GM_xmlhttpRequest与fetch
为什么选择GM_xmlhttpRequest而不是fetch?
跨域请求是最关键的差异。浏览器的 fetch 受同源策略(Same-Origin Policy)限制,向不同域名发请求时,如果目标服务器没有设置正确的 CORS 头,请求会被浏览器拦截。而 GM_xmlhttpRequest 运行在油猴扩展的特权上下文中,完全绕过同源策略和 CORS 限制,可以自由请求任何域名。这也是油猴脚本最常用它的原因——从第三方 API 抓取数据、跨站交互等场景用 fetch 根本做不到。
频繁GM_xmlhttpRequest的问题
GM_xmlhttpRequest请求会不断涌现出无数的webRequest 子事件,每重新注册一次,Chrome 就新建一组 /sNNN,Chrome 又把这些实例写进 [Secure Preferences],最终会导致该chrome profile彻底报废,表现形式就是一启动这个chrome就直接无响应
这是一个已知的 Chrome + Tampermonkey 的 bug,本质上是 Chrome 的 declarativeNetRequest / webRequest 注册机制和油猴扩展交互产生的问题。
根本原因:每次 GM_xmlhttpRequest 发起请求,Tampermonkey 需要通过 Chrome 的 webRequest API 注册规则来绕过 CORS。高频调用时,Chrome 不断创建新的 /sNNN 子事件实例,这些记录被持久化写入 Secure Preferences 文件。这个 JSON 文件膨胀到几十甚至几百 MB 后,Chrome 启动时解析它就会卡死。
怎么做到既要又要呢?
现在,我既要跨域请求,又要频繁请求,应该怎么做呢?Claude是这么说的。
方案一:本地服务加 HTTPS + CORS
把 localhost 的 端口 服务改成 HTTPS,用自签证书即可。这样就是 HTTPS → HTTPS,不存在混合内容问题,再加上 CORS 头,fetch 就能正常用了。
# 生成自签证书
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout localhost.key -out localhost.crt \
-days 3650 -subj '/CN=localhost'
```
然后在 Node 服务里用 `https.createServer` 启动,并加 CORS 头允许 `https://example.website`。首次访问时浏览器手动打开 `https://localhost:<port>` 信任一下证书就行。
方案二:本地起一个 HTTPS 反向代理
不改动现有服务,用 nginx 或 caddy 在本地起一个 HTTPS 代理,统一转发到后面的 HTTP 服务:
https://localhost:4000/api3000/* → http://localhost:3000/* https://localhost:4000/api3210/* → http://localhost:3210/*
油猴脚本里 fetch("https://localhost:4000/api3000/xxx"),只需要信任一次代理的证书。
方案三:WebSocket(最后我选择这个)
如果是频繁双向通信,在本地服务开一个 wss://localhost:xxxx(注意是 wss 不是 ws,同样需要自签证书),油猴脚本里建一条 WebSocket 长连接。只需建立一次连接,后续通信不走 HTTP,彻底避开 webRequest 问题,也适合你”不断互通”的场景。