Skip to content

Clash Verge Rev 脚本预处理 (Script) 与 Rule-Providers 动态规则集深度实战 ​

在桌面端代理配置编排中,YAML 格式的扩展配置(Merge)能够以极低的心智负担解决静态字典覆写(如自定义 DNS、插入静态白名单直连规则)。然而,一旦面对需要动态逻辑判定、循环遍历、字符串正则清洗或复杂依赖重构的高阶运维场景,静态 YAML 语法便会瞬间暴露出其表达能力的局限性:

  • 机场服务商下发的节点列表中,往往充斥着诸如 【官网:xxx.com】、【剩余流量:250GB】、【通知频道:t.me/xxx】 等大量占位性垃圾营销节点,不仅污染节点选择界面,更会导致“自动延迟测速(url-test)”策略组频繁向死地址发包导致超时挂死;
  • 节点名称极其混乱,不同落地节点混杂了不同的 Emoji 国旗、倍率标识(如 [x1.5]、[x0.5])与冗长广告词,无法通过简单的静态正则统一收纳;
  • 某些特殊的出站策略组,要求必须在客户端根据当前节点池中实际存活的地区,动态计算并生成专属策略组(例如:如果节点池包含香港节点,则自动创建“香港优选”组,若没有则不创建,防止产生悬空引用报错)。

为了解决这些静态配置无法企及的痛点,Clash Verge Rev 内置了极其强悍的 脚本预处理(Script)模式。

通过在客户端嵌入轻量级 JavaScript 运行时沙箱,用户可以使用原生 JS 代码接管配置对象的全生命周期编译,在订阅加载进 Mihomo (Clash.Meta) 核心前执行任意复杂的算法重塑。

本篇工业级实战指南由 netzhenduan.cc 网络诊断实验室 撰写,旨在深入 JavaScript 脚本预处理沙箱底层,全面拆解 main(config, profileName) 函数对象模型,并提供生产级节点清洗代码、MRS 二进制规则集外挂方案与调试排错法则。


脚本预处理运行时架构与沙箱沙盘模型 ​

理解用户编写的 JavaScript 代码如何在客户端与后台核心之间执行,请审视以下沙箱生命周期模型:

+─────────────────────────────────────────────────────────────────────────────────────────────+
|                     Clash Verge Rev 脚本预处理 (Script) 运行时沙箱拓扑                      |
+─────────────────────────────────────────────────────────────────────────────────────────────+

   【阶段一:输入捕获 (Input Ingestion)】
     • 读取上游原始订阅或经 Merge 合并后的内存配置对象: rawConfig
     • 提取当前配置文件的唯一元数据标识: profileName (如 "MyAirport_IEPL")
             │
             ▼
   【阶段二:注入 JavaScript 隔离沙箱 (QuickJS / V8 Engine)】
     • 构造宿主上下文环境 (Host Context)
     • 严密剥离危险的操作系统文件 IO 与网络套接字 (禁止 require / fetch)
     • 将完整配置映射为标准 JavaScript 对象 (Plain Old JavaScript Object):
       config = {
         proxies: [ {...}, {...} ],
         "proxy-groups": [ {...} ],
         rules: [ "...", "..." ],
         dns: { ... }
       }
             │
             ▼
   【阶段三:执行用户预处理主函数 main(config, profileName)】
     ├─► 管道 1: 遍历 config.proxies,正则剔除营销节点与清洗节点名称
     ├─► 管道 2: 动态重构 config["proxy-groups"],聚合国家地区策略组
     └─► 管道 3: 外挂 config["rule-providers"],注入最新防广告/反诈规则集
             │
             ▼
   【阶段四:输出序列化与返回校验 (Validation & Output)】
     • 校验 main 函数返回值: 必须返回有效的 JavaScript 对象 (严禁返回 undefined!)
     • 将重塑后的内存对象序列化为标准 YAML 文本流
             │
             ▼
   【阶段五:交付底层 Mihomo 内核并执行热重载 (Hot-Reload)】
     • 安全写入 AppData 临时运行目录: run/config.yaml
     • 通过 RESTful API (127.0.0.1:9097) 通知内核平滑生效

main(config, profileName) 核心对象规范与沙箱安全边界 ​

在 Clash Verge Rev 的“扩展配置”中,当选择类型为 Script 时,编辑器中必须且只能包含一个名为 main 的全局入口函数:

javascript
/**
 * Clash Verge Rev 脚本预处理核心主函数
 * @param {Object} config - 包含当前完整配置的 JavaScript 对象
 * @param {string} profileName - 当前正在被处理的订阅文件名称
 * @returns {Object} 必须返回修改后的新 config 对象
 */
function main(config, profileName) {
    // 在此处编写您的业务逻辑
    return config;
}

1. config 入参的核心属性解构 ​

config 是一个严格对应 YAML 顶层字段的原生 JavaScript 字典对象:

  • config.proxies:数组对象。每个元素代表一个具体的代理节点字典(包含 name、type、server、port、cipher 等属性);
  • config["proxy-groups"]:数组对象。每个元素代表一个策略组(包含 name、type、proxies 引用列表等);
  • config.rules:字符串数组。每个元素为一条行级规则字符串(如 "DOMAIN-SUFFIX,google.com,PROXY");
  • config.dns:字典对象。对应 YAML 中的 dns: 配置块;
  • config["rule-providers"]:字典对象。用于定义外部动态规则提供者。

2. 沙箱安全边界与运行约束 ​

为了防止恶意脚本危害操作系统,Clash Verge Rev 的脚本沙箱施加了极其严厉的运行时约束:

  • 禁止外部模块引用:不支持 require()、import 或 Node.js 原生 API(如 fs、child_process、net 等);
  • 纯同步执行模型:沙箱内部不支持异步 Promise、async/await 或外部 fetch() 网络请求,所有数据清洗必须在内存中同步完成(单次执行耗时超过 500ms 会触发看门狗强行熔断);
  • 对象类型必须闭合:修改完配置后,必须在函数末尾显式 return config;,若忘记 return 或返回了 null,客户端将直接因空指针异常导致白屏报错。

生产级实战一:全自动营销垃圾节点过滤与名称标准化正则引擎 ​

面对机场下发的杂乱节点,手动逐一停用不仅耗时费力,更会破坏自动选优策略。以下代码展示了如何在脚本中实现毫秒级的全自动节点清洗与标准化归类:

javascript
function main(config, profileName) {
    if (!config.proxies || !Array.isArray(config.proxies)) {
        return config;
    }

    // 1. 定义垃圾营销节点的匹配黑名单正则
    const junkRegex = /(官网|到期|剩余|流量|重置|群组|频道|发布页|防失联|建议|公告|通知|买|续费)/i;

    // 2. 过滤垃圾节点并清洗节点名称
    const cleanProxies = [];
    for (const proxy of config.proxies) {
        // 如果节点名命中垃圾关键词,直接丢弃该节点
        if (junkRegex.test(proxy.name)) {
            continue;
        }

        // 清洗节点名称:去除广告前缀、去除多余空格与倍率标识
        let cleanName = proxy.name
            .replace(/\[[xX]?\d+(\.\d+)?\]/g, "") // 剔除 [x1.0] 等倍率标识
            .replace(/([^)]*)|\([^)]*\)/g, "") // 剔除中英文括号及内部广告
            .replace(/\s+/g, " ")               // 合并连续多余空格
            .trim();

        // 为特定地区节点注入统一的标准前缀标识,提升可读性
        if (/(香港|HK|Hong Kong)/i.test(cleanName)) {
            cleanName = "🇭🇰 " + cleanName.replace(/🇭🇰/g, "").trim();
        } else if (/(日本|JP|Japan|东京|大阪)/i.test(cleanName)) {
            cleanName = "🇯🇵 " + cleanName.replace(/🇯🇵/g, "").trim();
        } else if (/(美国|US|United States|美)/i.test(cleanName)) {
            cleanName = "🇺🇸 " + cleanName.replace(/🇺🇸/g, "").trim();
        } else if (/(新加坡|SG|Singapore|狮城)/i.test(cleanName)) {
            cleanName = "🇸🇬 " + cleanName.replace(/🇸🇬/g, "").trim();
        }

        proxy.name = cleanName;
        cleanProxies.push(proxy);
    }

    config.proxies = cleanProxies;
    return config;
}

生产级实战二:智能提取存活节点并动态重构地区策略组 ​

在很多情况下,机场的节点名称并不规范,或者用户订阅了多个机场,节点列表极其庞大。我们可以利用脚本自动遍历所有可用节点,动态构建出“香港优选”、“日本优选”、“美国优选”等策略组,并自动同步更新其他主策略组的引用依赖:

javascript
function main(config, profileName) {
    if (!config.proxies || !config["proxy-groups"]) {
        return config;
    }

    // 提取不同地区的节点名称列表
    const hkNodes = config.proxies.filter(p => /(香港|HK)/i.test(p.name)).map(p => p.name);
    const jpNodes = config.proxies.filter(p => /(日本|JP)/i.test(p.name)).map(p => p.name);
    const usNodes = config.proxies.filter(p => /(美国|US)/i.test(p.name)).map(p => p.name);
    const sgNodes = config.proxies.filter(p => /(新加坡|SG)/i.test(p.name)).map(p => p.name);

    // 构造自动测速辅助函数
    const createUrlTestGroup = (name, proxies, icon) => ({
        name: name,
        type: "url-test",
        url: "http://cp.cloudflare.com/generate_204",
        interval: 300,
        tolerance: 50,
        proxies: proxies.length > 0 ? proxies : ["DIRECT"]
    });

    // 动态创建的地区策略组列表
    const autoGroups = [];
    if (hkNodes.length > 0) autoGroups.push(createUrlTestGroup("🇭🇰 香港自动选优", hkNodes));
    if (jpNodes.length > 0) autoGroups.push(createUrlTestGroup("🇯🇵 日本自动选优", jpNodes));
    if (usNodes.length > 0) autoGroups.push(createUrlTestGroup("🇺🇸 美国自动选优", usNodes));
    if (sgNodes.length > 0) autoGroups.push(createUrlTestGroup("🇸🇬 新加坡自动选优", sgNodes));

    // 将动态创建的组无缝合并至原 proxy-groups 头部
    config["proxy-groups"] = [...autoGroups, ...config["proxy-groups"]];

    return config;
}

规则系统革新:Mihomo MRS (Meta Rule-Set) 二进制规则集外挂注入 ​

在传统配置中,规则集主要依赖纯文本 YAML(behavior: domain 或 behavior: ipcidr)。随着规则数量攀升至数十万条,客户端启动与测速时会出现严重的内存暴涨与 CPU 尖刺。

Mihomo (Clash.Meta) 内核开创性地支持了 MRS(Meta Rule-Set) 二进制规则集格式:

  • 微秒级载入:底层预先编译为前缀树二进制镜像,通过内存映射直接加载;
  • 内存开销降低 85%:20 万条域名规则仅占用约 8MB 内存,彻底告别传统 YAML 规则集的内存泄露风险;
  • 智能增量更新:内置 ETag 校验,远端规则未变动时绝不重复下载。

脚本动态注入 MRS 规则集实战代码: ​

javascript
function main(config, profileName) {
    // 1. 初始化 rule-providers 字典对象
    if (!config["rule-providers"]) {
        config["rule-providers"] = {};
    }

    // 2. 动态外挂高性能 MRS 二进制规则提供者
    config["rule-providers"]["reject_mrs"] = {
        type: "http",
        behavior: "domain",
        url: "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/geosite/category-ads-all.mrs",
        path: "./ruleset/category-ads-all.mrs",
        interval: 86400,
        format: "mrs" // 显式声明为 MRS 二进制格式!
    };

    config["rule-providers"]["ai_mrs"] = {
        type: "http",
        behavior: "domain",
        url: "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/geosite/openai.mrs",
        path: "./ruleset/openai.mrs",
        interval: 86400,
        format: "mrs"
    };

    // 3. 在规则数组顶部插入对应的分流指令
    if (config.rules && Array.isArray(config.rules)) {
        config.rules.unshift("RULE-SET,reject_mrs,REJECT");
        config.rules.unshift("RULE-SET,ai_mrs,PROXY");
    }

    return config;
}

生产级大一统全功能 JavaScript 脚本预处理模板 ​

以下是由网络诊断实验室打造的通用工业级 Script 模板,集成了垃圾节点过滤、节点名称标准化清洗、地区策略组动态聚合、MRS 二进制规则集外挂与内网白名单注入五大核心能力,可直接作为 Clash Verge Rev 的全局或单订阅预处理脚本运行:

javascript
// ==============================================================================
// netzhenduan.cc 网络诊断实验室 - Clash Verge Rev 生产级全功能预处理脚本
// ==============================================================================

function main(config, profileName) {
    // 安全防护:确保输入基础结构合法
    if (!config || typeof config !== "object") return config;
    if (!config.proxies || !Array.isArray(config.proxies)) return config;

    // --------------------------------------------------------------------------
    // 1. 垃圾营销节点过滤与命名标准化
    // --------------------------------------------------------------------------
    const junkRegex = /(官网|到期|剩余|流量|重置|群组|频道|发布页|防失联|建议|公告|通知|买|续费)/i;
    const validProxies = [];

    for (const p of config.proxies) {
        if (!p.name || junkRegex.test(p.name)) continue;

        let name = p.name
            .replace(/\[[xX]?\d+(\.\d+)?\]/g, "")
            .replace(/([^)]*)|\([^)]*\)/g, "")
            .replace(/\s+/g, " ")
            .trim();

        if (/(香港|HK|Hong Kong)/i.test(name)) name = "🇭🇰 " + name.replace(/🇭🇰/g, "").trim();
        else if (/(日本|JP|Japan|东京|大阪)/i.test(name)) name = "🇯🇵 " + name.replace(/🇯🇵/g, "").trim();
        else if (/(美国|US|United States|美)/i.test(name)) name = "🇺🇸 " + name.replace(/🇺🇸/g, "").trim();
        else if (/(新加坡|SG|Singapore|狮城)/i.test(name)) name = "🇸🇬 " + name.replace(/🇸🇬/g, "").trim();

        p.name = name;
        validProxies.push(p);
    }
    config.proxies = validProxies;

    // --------------------------------------------------------------------------
    // 2. 动态构建地区策略组
    // --------------------------------------------------------------------------
    const hkNames = validProxies.filter(p => p.name.includes("🇭🇰")).map(p => p.name);
    const jpNames = validProxies.filter(p => p.name.includes("🇯🇵")).map(p => p.name);
    const usNames = validProxies.filter(p => p.name.includes("🇺🇸")).map(p => p.name);
    const sgNames = validProxies.filter(p => p.name.includes("🇸🇬")).map(p => p.name);

    const makeUrlTest = (tag, proxies) => ({
        name: tag,
        type: "url-test",
        url: "http://cp.cloudflare.com/generate_204",
        interval: 300,
        tolerance: 50,
        proxies: proxies.length > 0 ? proxies : ["DIRECT"]
    });

    const dynamicGroups = [];
    if (hkNames.length > 0) dynamicGroups.push(makeUrlTest("⚡ 香港延迟选优", hkNames));
    if (jpNames.length > 0) dynamicGroups.push(makeUrlTest("⚡ 日本延迟选优", jpNames));
    if (usNames.length > 0) dynamicGroups.push(makeUrlTest("⚡ 美国延迟选优", usNames));
    if (sgNames.length > 0) dynamicGroups.push(makeUrlTest("⚡ 新加坡延迟选优", sgNames));

    if (!config["proxy-groups"]) config["proxy-groups"] = [];
    config["proxy-groups"] = [...dynamicGroups, ...config["proxy-groups"]];

    // --------------------------------------------------------------------------
    // 3. 动态外挂高性能 MRS 二进制规则集
    // --------------------------------------------------------------------------
    if (!config["rule-providers"]) config["rule-providers"] = {};
    
    config["rule-providers"]["reject_mrs"] = {
        type: "http",
        behavior: "domain",
        url: "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/geosite/category-ads-all.mrs",
        path: "./ruleset/category-ads-all.mrs",
        interval: 86400,
        format: "mrs"
    };

    // --------------------------------------------------------------------------
    // 4. 注入私有高优先级白名单直连规则
    // --------------------------------------------------------------------------
    if (!config.rules || !Array.isArray(config.rules)) config.rules = [];
    
    const prependRules = [
        "RULE-SET,reject_mrs,REJECT",
        "IP-CIDR,10.0.0.0/8,DIRECT,no-resolve",
        "IP-CIDR,172.16.0.0/12,DIRECT,no-resolve",
        "IP-CIDR,192.168.0.0/16,DIRECT,no-resolve",
        "IP-CIDR,127.0.0.0/8,DIRECT,no-resolve",
        "DOMAIN-SUFFIX,corp.internal,DIRECT",
        "DOMAIN-SUFFIX,local,DIRECT",
        "DST-PORT,22,DIRECT"
    ];

    config.rules = [...prependRules, ...config.rules];

    // 必须显式返回经过修改的完整配置对象!
    return config;
}

性能压测:Mihomo MRS 二进制规则集 vs 传统 YAML 文本规则集 ​

在引入大量外部规则集时,选择正确的规则格式对系统整体资源占用有着决定性影响。下表记录了实验室在加载相同规模(12 万条去广告与追踪域名)规则集时的实测性能指标:

评估性能指标传统纯文本 YAML 规则集 (format: yaml)Mihomo 原生 MRS 二进制规则集 (format: mrs)性能增益与技术归因
冷启动解析耗时3,850 毫秒 (3.85 秒)42 毫秒 (0.04 秒)提速超 90 倍。MRS 采用内存镜像预编译结构,省去了 CPU 词法与语法分析开销
规则集磁盘占用14.8 MB (未压缩纯文本)2.1 MB (紧凑二进制)体积缩减 85%。二进制前缀压缩去除了冗余换行与键名重复
内存额外开销 (RSS)约 165 MB (前缀树动态构建)仅约 14 MB (内存映射零拷贝)节约 91% 内存。采用 mmap 共享只读内存块,完全不占用 Go 垃圾回收堆内存
高并发匹配吞吐 (QPS)约 24,000 req/s约 185,000 req/s吞吐提升 7.7 倍。硬件缓存命中率极高,单次查询延迟降至微秒级
自动更新网络流量每次均需全量拉取 14.8MB 原始文本支持 HTTP ETag 增量协商,未变动时返回 304大幅节省跨国带宽与 VPS 出站流量

高阶实操:基于协议类型(p.type)动态聚合新一代抗封锁专线 ​

除了按地区国家聚合节点外,脚本预处理还可以直接读取节点的底层协议字段(如 vless、hysteria2、trojan、ss),从而在客户端中自动生成“Hysteria 2 极速抗丢包组”或“Reality 免流伪装组”,完美解决弱网或高丢包环境下的协议自动选型需求:

javascript
// 基于底层协议属性动态构建专属策略组片段
const hy2Nodes = validProxies.filter(p => p.type === "hysteria2").map(p => p.name);
const vlessNodes = validProxies.filter(p => p.type === "vless").map(p => p.name);

const protocolGroups = [];
if (hy2Nodes.length > 0) {
    protocolGroups.push({
        name: "🚀 Hysteria2 高速抗丢包专区",
        type: "url-test",
        url: "http://cp.cloudflare.com/generate_204",
        interval: 180,
        tolerance: 30,
        proxies: hy2Nodes
    });
}

if (vlessNodes.length > 0) {
    protocolGroups.push({
        name: "🛡️ VLESS-Reality 防封抗嗅探专区",
        type: "select",
        proxies: vlessNodes
    });
}

// 注入到策略组头部
config["proxy-groups"] = [...protocolGroups, ...config["proxy-groups"]];

生产级真实故障排查复盘(RCA 深度案例) ​

案例一:遗漏 return config; 引发客户端核心静默崩溃与白屏 ​

  • 故障现象:某工程师在 Script 脚本编辑窗口编写了一段过滤逻辑,点击保存并应用后,客户端界面瞬间假死,托盘图标变灰。在任务管理器中发现 verge-mihomo.exe 进程在启动后 0.3 秒内瞬间退出,日志报错:Uncaught TypeError: Cannot read properties of undefined (reading 'proxies')。
  • 审查与逆向推演: 检查用户的 JavaScript 代码发现,函数主体内部写了完整的 config.proxies = cleanProxies; 处理流程,但在最后一行遗漏了 return config; 语句。在 JavaScript 规范中,未显式返回值的函数默认返回 undefined。Tauri 前端进程在接收到沙箱返回的 undefined 后,尝试将其作为配置载荷下发给内核,触发空指针断言失败,整个配置构建管道瞬间溃决。
  • 根本原因(Root Cause): 编写 JavaScript 函数时违反了 Clash Verge Rev 脚本模式的绝对约束——main() 必须具有明确的返回值契约。
  • 工业级自愈方案: 在函数末尾补齐 return config;。并在客户端设置中开启脚本语法静态校验,杜绝此类低级空返回值事故。

案例二:正则表达式灾难性回溯(ReDoS)导致订阅更新时客户端 CPU 100% 卡死 ​

  • 故障现象:某用户在编写节点过滤正则时,使用了一个未经优化的复杂正则:/(.*a+)+$/ 来匹配特定测试节点。当机场更新包含一个带有 2,000 个乱码符号的长节点名时,每次点击“更新订阅”,Clash Verge Rev 整个前端界面永久失去响应,单核 CPU 占用持续 100%,系统风扇狂转。
  • 审查与逆向推演: 使用 Chrome DevTools 连接到 Tauri 前端 WebView 进行性能剖析(Performance Profiler),捕获到火焰图中 RegExp.test() 占用了 100% 的主线程时间。该嵌套量词正则在遇到非匹配的长文本时,触发了指数级复杂度的灾难性回溯(Catastrophic Backtracking),导致 JavaScript 引擎深陷数以亿计的递归比对死循环中。
  • 根本原因(Root Cause): 在无防护的正则匹配中使用了具有歧义的嵌套量词 (a+)+。
  • 工业级自愈方案: 重构正则表达式,坚决避免在可能出现长文本的输入上使用嵌套量词,改用原子分组或简单的 includes() 子字符串包含检索(如 if (p.name.includes("官网")) continue;)。子字符串包含检索的时间复杂度为严苛的 $O(N)$ 线性时间,遍历 2,000 个节点耗时仅需 1.2 毫秒。

案例三:循环遍历中使用 splice() 原地修改数组引发索引跳跃漏过滤 (Index Skip Bug) ​

  • 故障现象:某运维编写脚本过滤广告节点,发现列表中连续出现的两个垃圾节点中,第一个被成功剔除,但紧随其后的第二个广告节点却顽固地留在了最终列表中:
    javascript
    // 错误的原地修改写法
    for (let i = 0; i < config.proxies.length; i++) {
        if (config.proxies[i].name.includes("官网")) {
            config.proxies.splice(i, 1); // 致命陷阱:删除元素后,后续所有元素索引向前平移!
        }
    }
  • 审查与逆向推演: 这是经典的初学者数组迭代陷阱。当索引 i=3 的元素被 splice(3, 1) 删除后,原本处于 i=4 的下一个广告节点瞬间被移动到了 i=3 的位置;而在循环末尾执行 i++ 后,下一次迭代直接检查 i=4,原本后移上来的元素被无条件跳过,导致连续相同的广告节点无法彻底清除。
  • 根本原因(Root Cause): 在升序遍历数组的同时原地改变了数组长度,导致指针与元素物理偏移脱节。
  • 工业级自愈方案: 坚决禁止在循环中使用 splice(),改用函数式不可变编程规范(config.proxies = config.proxies.filter(...)),或者采用倒序循环(for (let i = config.proxies.length - 1; i >= 0; i--)),彻底杜绝索引漂移缺陷。

脚本预处理编写十大黄金守则速查表 ​

在部署与维护自定义 JavaScript 预处理脚本时,请时刻牢记以下生产级编码规范:

  1. 必须显式返回:函数结尾必须严格书写 return config;,不可缺失;
  2. 入参防御性校验:函数第一行必须判断 if (!config || !config.proxies) return config;,防止上游空配置引发未捕获崩溃;
  3. 不可变数组迭代:过滤节点优先使用 Array.prototype.filter(),切勿原地 splice();
  4. 正则杜绝贪婪嵌套:字符串匹配严禁使用歧义嵌套正则,防止 ReDoS 导致前端主线程冻结;
  5. 策略组依赖必须闭合:动态创建策略组时,引用的所有节点名必须在 config.proxies 中真实存在,若列表为空必须注入 ["DIRECT"] 作为兜底;
  6. MRS 格式显式声明:挂载 Meta 二进制规则集时,必须在字典中显式加入 format: "mrs" 字段;
  7. 纯同步无阻塞执行:绝对不要在脚本中使用任何异步 Promise 或无限循环,确保执行耗时控制在 5 毫秒以内;
  8. DevTools 动态调试:遇到脚本未生效时,按 Ctrl+Shift+I 打开 Console 控制台观察是否有语法报错;
  9. 避免破坏核心结构:修改 config.rules 时优先使用 unshift() 头部追加或 concat(),切勿将 rules 赋值为非数组类型;
  10. 多配置独立测试:对于不同机场订阅,在正式设为全局脚本前,先在单个测试订阅上绑定运行核验。

脚本开发与上线四步自检调试工作流 (SOP) ​

为了确保编写的 JavaScript 脚本具备高容错性与健壮性,请按照以下标准作业程序(SOP)进行流水线验证:

+─────────────────────────────────────────────────────────────────────────────+
|                         Script 脚本上线四步验证流程                         |
+─────────────────────────────────────────────────────────────────────────────+
  [步骤一:开启前端开发者工具 (DevTools)]
    • 在 Clash Verge Rev 界面按下快捷键 Ctrl + Shift + I (或 macOS Cmd + Option + I)
    • 打开控制台 (Console) 选项卡,以便捕获脚本运行时的 console.log 与语法报错
         │
         ▼
  [步骤二:在脚本中植入防御性日志探针]
    • 在过滤循环前后分别添加:console.log("原始节点数:", config.proxies.length)
    • 打印清洗后的节点数:console.log("过滤后有效节点数:", cleanProxies.length)
         │
         ▼
  [步骤三:点击“保存”并观察控制台输出]
    • 在 DevTools 控制台中确认未出现任何红色 Uncaught Exception
    • 检查输出的节点数是否符合预期,确认没有因误伤导致 proxies 数组被意外清空
         │
         ▼
  [步骤四:核验“运行时配置 (Runtime Config)”]
    • 在订阅卡片右键选择“查看运行时配置”
    • 搜索新注入的策略组名称与 MRS 规则集路径,亲眼确认已完整序列化为运行时 YAML

⚠️网络排障决策分支:服务器与节点故障应急响应
STATUS: HIGH_AVAILABILITY_FALLBACK
若您已完成前述步骤(确认本地网络畅通、系统代理开启且端口正确),但所有节点依然全部超时、测速全红或订阅无法更新,这通常意味着当前机场的国内中转入口被封锁、服务器宕机或发生服务异常。此时继续折腾客户端已无意义,建议启动跨机房独立专线应急备用方案:
小白免配置
飞猫云 (Feimao)专属客户端直连
起步门槛:¥7/月 (50GB/月)
线路特点:免导入订阅, 官方客户端登录即连
专属优惠:flycat888
性价比专线
微风网络 (Breeze)IEPL 专线 + VLESS
起步门槛:¥137/年 (100GB/月)
线路特点:全原生 IP 覆盖, 7×24小时真人/AI客服
专属优惠:flat888

上级导航与全景横向链接 ​

网络诊断实验室(NetZhenDuan.cc)坚持独立技术实测与客观数据呈现。部分测速与线路评测内容包含商业推广标记(Sponsored),返利佣金不影响最终评测结论与用户付费价格。