REX 模块说明
好消息:这一章是整个教程里最轻松的一章。因为 REX 的模块和你已经学过的 Surge 模块,基本上是“一家人”。
第一部分这一章学什么
一句话目标:知道 REX 模块和 Surge 模块基本同构(长得几乎一样)——学完 Surge 那一章,REX 的模块就能看懂八九成;再记住写 REX 模块的三条注意事项,这一章就过关了。
三条注意事项,先混个眼熟,后面还会细讲:
- 先拿模板改:写 REX 模块时,先找一个官方或社区现成的模块当模板,在它上面改,别白手起家。
- 先小范围测试:脚本先只对单个域名生效,确认
$done(脚本收尾的信号)的行为正常,再全量启用。 - 三端兼容用判端模板:同一个脚本想同时在 Surge、Loon、REX 上跑,用第十五章的判端模板。
第二部分概念:和 Surge 一脉相承
REX 是一个相对新的代理工具,它的模块体系直接沿用了 Surge 的设计:文件的骨架一样、声明的写法一样、脚本能用的接口也一样。你在 Surge 那一章学的东西,到这里几乎原样能用。
文件结构:一模一样的骨架
REX 模块文件同样是 #!name 开头(写模块名字),然后用方括号分成几段:
#!name=示例模块 #!desc=演示用的模块 [Rule] DOMAIN,example.com,DIRECT [URL Rewrite] ^https://old\.example\.com https://new.example.com 302 [Script] 示例脚本 = type=http-response, pattern=^https://api\.example\.com, script-path=demo.js, requires-body=true [MITM] hostname = api.example.com
[Rule]:规则段,决定哪些流量走代理、哪些直连。[URL Rewrite]:网址重写段,把一个网址换成另一个。[Script]:脚本段,声明什么时候运行哪个脚本。[MITM]:解密段,写上要解密查看的域名(hostname 就是“主机名”,也就是域名)。
这四段的名字、顺序、写法,和 Surge 模块完全一致。
脚本声明的参数:含义一致
[Script] 段里每个参数的意思,REX 和 Surge 也是一样的:
脚本能用的接口:还是那一套
脚本里面和外界打交道的工具,REX 也沿用 Surge 的那套名字和用法:
所以结论很简单:学完 Surge 那一章,REX 的模块就能看懂个八九成。剩下的那一两成,是下面这个提醒。
$done 的参数、定时任务 cron 的写法,在某个版本上可能还不支持或者写法略有不同。遇到“在 Surge 上好好的、搬到 REX 不灵”,先怀疑版本支持问题,去对照当前版本的说明。
写 REX 模块的三条注意事项
- 先拿模板改,别白手起家。找一个官方或社区现成的、能跑的 REX 模块,在它上面改名字、改域名、换脚本。骨架现成,不容易写错格式。
- 先小范围测试。新脚本先让 pattern 只匹配单个域名,跑通了、确认
$done收尾行为正常(不卡住、不报错),再放开范围全量启用。 - 三端兼容用判端模板。同一个脚本想同时兼容 Surge、Loon、REX,不要写三份,用第十五章的判端模板:一份代码先判断自己在哪个 App 里,再走对应的写法。
第三部分动手试:并排对照找不同
下面是同一个功能的脚本声明,左边按 Surge 模块的写法,右边按 REX 模块的写法。你先自己逐字看一遍,再回答下面的问题。
去广告 = type=http-response, pattern=^https://api\.example\.com/ads, script-path=remove-ads.js, requires-body=true
去广告 = type=http-response, pattern=^https://api\.example\.com/ads, script-path=remove-ads.js, requires-body=true
两行拆开看:等号左边是脚本起的名字;type 是运行的时机;pattern 是要匹配的网址;script-path 是脚本文件;requires-body 表示需要正文。
问题:这两行声明,有什么不同?
再试一个:用代码检查模块段落齐不齐
一个模块文件最容易出的错,是漏写了某一段。下面这段 JavaScript 用字符串的 includes 方法(意思是“看看有没有包含”)逐个检查 [Rule]、[Script]、[MITM] 三段在不在。你可以改下面的模块文本,再点“运行”看结果。
// 要检查的三个段落 let 段落 = ["[Rule]", "[Script]", "[MITM]"]; let 缺的 = []; // 逐个检查:文本里包含这一段吗? for (let 段 of 段落) { if (文本.includes(段)) { console.log("✓ " + 段 + " 有"); } else { console.log("✗ " + 段 + " 缺了"); 缺的.push(段); } } if (缺的.length === 0) { console.log("三个段落齐了!"); } else { console.log("还缺:" + 缺的.join("、")); }
模块文本(可以直接改,比如把某一行删掉再运行):
看懂这段代码的关键就一个词:includes。它问的是“这段长文本里,包不包含这个小词”,答案只有 true(包含)或 false(不包含)两种。
第四部分练一练
第五部分适用案例
把一个 Surge 模块改成 REX 模块,要动哪几处?
- 先把 Surge 模块和一个现成的 REX 模块并排对照:你会发现
#!name开头和四段结构完全一样,先放心。 [Rule]、[URL Rewrite]、[Script]的声明原样保留,参数不用改。- 只逐个核对个别字段:这个模块如果用到了 cron 定时、或者
$done的特殊参数,去查当前 REX 版本支不支持、写法是否相同。 [MITM]的 hostname 照抄,然后按案例二的方法小范围测一遍再全量启用。
新脚本先在小范围测试 $done,再全量启用
- 把脚本声明里的
pattern先只写单个域名,比如只匹配^https://api\.example\.com/test这一个测试地址。 - 实际访问一次,看脚本是否正常运行、
$done是否正常收尾:页面能正常打开、不转圈卡住、不报错,就是通过。 - 确认没问题后,再把
pattern放开到正式范围,全量启用。出问题时影响面也只限于那一个域名,好排查。
一份脚本同时跑 Surge、Loon、REX
- 你有一个去广告脚本,想在三个 App 里都用——不要复制三份分别维护,改一处就要改三次,早晚改漏。
- 用第十五章的判端模板:脚本开头先判断自己正跑在哪个 App 里,再调用那个 App 对应的写法。
- 三个 App 的模块里,
script-path都指向这同一份脚本。以后修 bug、加功能,只改这一个文件,三端同时生效。