JAVASCRIPT · 第十五章

多端兼容写法
一份代码,跑三个地方

Surge、Loon、REX 里面的小脚本长得差不多,但细节不一样。这一章教你把差异收进一个小角落,主干只写一遍,以后改起来只改一处。

⏱ 约 12 分钟📱 手机可学 · 可点可跑🔤 英文词当符号背,有中文注释
🎯 这一章学什么

会写一份代码跑 Surge、Loon、REX 三处,记住三件套:判端、读参数、适配层。

判端先看自己在哪
读参数两种给法都接
适配层差异只放这里
2

先懂概念:主逻辑一份,差异进适配层

这是社区脚本最常见的通用结构,先看懂,再动手跑。

① 判断当前在哪个客户端里 —— 判端

每个工具都会悄悄放一个专属变量在环境里。看到哪个变量在,就知道自己在哪。

判端 · 复制就能用
const isLoon = typeof $loon !== "undefined"; // $loon 在,说明在 Loon 里
const isSurge = typeof $httpClient !== "undefined" && !isLoon; // 有发请求工具、又不在 Loon,才算 Surge
为什么用 typeof,不直接写 $loon?
直接写一个不存在的变量,程序会立刻报错停掉。用 typeof 去问,就算变量不存在,它也只会回答 "undefined"(意思是“没有这个东西”),不会报错。这就像先敲门确认屋里有没有人,而不是直接闯进去。

记法:$loon → Loon 的身份证;$httpClient → Surge 也有的发请求工具,所以要再加一个“不在 Loon”的条件,两个工具才不会认错。

② 读参数 —— 兼容字符串与对象两种给法

你在插件或模块里填的参数,会通过 $argument 传进来。但不同客户端给法不一样:有的给一串文字(字符串),有的直接给一个对象。都要接住。

读参数 · 通用写法
let arg = {}; // 先准备一个空对象,兜底用
if (typeof $argument !== "undefined") { // 有参数传进来才读
  if (typeof $argument === "object") {
    arg = $argument; // 本来就是对象,直接用
  } else {
    try {
      arg = JSON.parse($argument); // 是字符串,就转成对象
    } catch (e) {
      arg = {}; // 转失败也不崩,用空对象继续
    }
  }
}

JSON.parse 是“把文字转成对象”的工具;try / catch 是保险丝 —— 试着转,转不动就走兜底,不让整个脚本停掉。

③ 发通知与交卷 —— 各端写法一致

这一块最省心,不用判端:

通知 + 交卷 · 三端通用
$notification.post("标题", "", "内容"); // 发一条通知,三端都这样写
$done(); // 告诉客户端:我做完了,可以交卷了
💡 所以适配层真正要操心的只有两件事:判端和读参数。通知和交卷直接用,不用为每个客户端再写一份。
3

动手试:点「运行」,真的跑给你看

下面三段代码会在这个页面里真正执行,出错也会用中文告诉你错在哪。

试一试 1 · 判端,在这个页面里会是什么结果?

判端演示
const isLoon = typeof $loon !== "undefined";
const isSurge = typeof $httpClient !== "undefined" && !isLoon;
console.log("isLoon:" + isLoon);
console.log("isSurge:" + isSurge);
先猜一下:两个是 true 还是 false?
运行结果
在这个页面里两个工具都不存在,所以两个判断都是“不在”(false)。把同一行代码放到 Loon 里跑,isLoon 就会变成 true —— 代码没变,环境变了,结果就变了,这就是判端的用处。

试一试 2 · 读参数,两种给法都接住

先给字符串,再给对象,看最后读到的 vip 是不是一样。

读参数演示
// 给法一:字符串(Surge 常见)
let raw1 = '{"vip":true}';
let arg1 = JSON.parse(raw1);
console.log("字符串给法,vip 是:" + arg1.vip);

// 给法二:对象(Loon 插件常见)
let arg2 = { vip: true };
console.log("对象给法,vip 是:" + arg2.vip);
两种给法,最后都应该读到 true
运行结果

试一试 3 · 主逻辑 + 适配层,小例子跑通全流程

主逻辑只管干活(判断要不要放行),适配层只管判端和读参数,两边分工不打架。

完整小例子
// —— 主逻辑:只写一份,哪端都用它 ——
function mainLogic(arg) {
  if (arg.vip) {
    console.log("主逻辑:你是会员,放行 ✅");
  } else {
    console.log("主逻辑:不是会员,拦住");
  }
}

// —— 适配层:只做判端 + 读参数 ——
const isLoon = typeof $loon !== "undefined";
console.log("适配层:在 Loon 里吗?" + isLoon);
let arg = { vip: true }; // 假装这是读进来的参数
mainLogic(arg);
console.log("适配层:最后交卷 $done()");
看输出顺序:先适配层,再主逻辑
运行结果
4

练一练:5 道题,点一下就知道对错

选错会告诉你错在哪,选对也会讲为什么对。

🏆 已答对 0 / 5 题
第 1 题 · 判端

typeof $loon !== "undefined" 是在判断什么?

第 2 题 · 为什么用 typeof

为什么用 typeof 判断,而不是直接写 $loon?

第 3 题 · 参数长什么样

$argument 在不同客户端里,可能是什么?

第 4 题 · try / catch 的作用

读 $argument 时,为什么要包一层 try / catch?

第 5 题 · 通用结构

多端兼容的通用结构是什么?

5

适用案例:这套写法真实用在哪

不是背概念,是你以后写脚本、改脚本都会碰到的场景。

🧹

一份去广告脚本,跑 Surge + Loon + REX

判端只在开头做一次,后面删广告的主逻辑三个客户端完全一样。用户换工具,脚本不用重写,复制过去就能跑。

🎛️

同一个开关,两种给法都要读懂

比如一个“是否开启去广告”的开关:在 Surge 里传进来是字符串,要用 JSON.parse 转;在 Loon 插件里传进来直接是对象,拿来就用。兼容写法两种都接得住,不会一边好使、一边失效。

🔧

适配层只做三件事,不越界

适配层只负责判端、读参数,剩下的通知用 $notification、交卷用 $done,各端一致直接写。以后某个客户端改了给法,只改适配层那几行,主逻辑一行都不用动。

一句话小结

多端兼容不难记:先判端、再读参数,差异只进适配层,主逻辑永远只写一份 —— 这样一份代码,才能稳稳跑遍 Surge、Loon 和 REX。