节点梯 NODE LADDER // 中文网络协议 · 路由 · 测速基准 · 简讯知识库
客户端 / CLIENTSREADING ESTIMATE: 8 MIN
RELEASED:

Clash Verge Rev扩展脚本进阶:自动化增补自定义规则与节点过滤

SPECIFICATION ABSTRACT // 概要解析

深入剖析Clash Verge Rev中Merge与Script扩展脚本的执行机制,提供基于JavaScript的配置对象动态重构、正则节点过滤与高优先级规则自动化注入实战。

CURATOR:Node Ladder Editorial
CONTENT:技术解读

在日常使用基于 Mihomo(Clash Meta)内核的桌面客户端 Clash Verge Rev 时,几乎所有用户都会遇到一个痛苦的维护难题:服务商提供的远程订阅配置通常每隔几天就会自动更新。一旦订阅刷新,用户之前辛辛苦苦在本地配置文件中手动添加的自定义分流规则、特定的 DNS 参数或本地策略组就会被远端覆盖得一干二净。如果选择关闭自动更新,又会错过服务商新增的优质节点与节点维护通知。为了打破“本地修改与订阅更新不可兼得”的恶性循环,Clash Verge Rev 引入了强大的“扩展脚本(Script / Merge)”机制。本文将系统拆解扩展脚本的运行生命周期,并给出生产环境下的配置覆写实战指南。

一、扩展配置的微观执行时序:Merge 与 Script

要自如地编写扩展逻辑,首先必须搞清楚客户端在将最终配置投递给内核前所经历的完整生命周期流水线。

在 Clash Verge Rev 中,配置文件的最终生成是一个多层叠加的合成过程(Overlay Pipeline):

第一阶段是基础订阅拉取(Remote Subscription)。客户端通过 HTTP 协议从服务商节点拉取原始的 YAML 文本文件。在拉取成功后,客户端的解析引擎会将其转换并反序列化为内部的 JavaScript 对象树结构。这一层完全由远端服务商控制,任何直接修改都会在下一次订阅刷新时被无情覆盖。

第二阶段是 Merge 静态合并层。如果用户配置了 Merge 扩展,客户端会利用经典的深度对象合并(Deep Merge)算法,将用户预定义的静态 YAML 代码片段,强行递归合并到原始订阅对应的键值上。Merge 的优势在于声明式语法简单直观,适合覆写全局 DNS 模块或开启 TUN 虚拟网卡等固定参数。然而,Merge 机制的致命短板在于缺乏任何动态逻辑能力,无法执行条件判断、数组精确索引插入、字符串正则表达式匹配或节点剔除。

第三阶段是 Script 动态脚本层。如果配置了 Script 扩展,客户端会将经过初步合并的完整配置对象,作为入参传递给一段由用户编写的 JavaScript 处理函数 main(params)。用户可以在这段 JS 代码中对整个配置树进行任意编程操控。例如,遍历所有的底层节点数组,利用正则表达式根据延迟、国家代号或关键词进行精细化分类;在分流规则数组的最前端动态插入最高优先级的专属规则;甚至根据节点可用性动态生成多层嵌套的策略组结构。函数执行完毕后,必须返回一个合法的配置对象。

第四阶段是内核运行时装载(Runtime Launch)。经过脚本处理完成的最终配置对象,被客户端重新序列化为标准的 YAML 格式文件,并通过内核提供的外部控制接口(External Controller API)以热加载的方式注入到正在运行的 Mihomo 内核进程中,即时生效。

通过这种“订阅为底、脚本为修饰”的解耦架构,无论远程订阅如何频繁拉取更新,用户的个人定制逻辑都会在每次更新后自动重新执行并精确生效,从根本上实现了配置的高可靠性与长期免维护。

二、编写 Script 扩展脚本的核心规范与上下文环境

在 Clash Verge Rev 中编写 Script 扩展时,使用的是标准沙箱化的轻量级 JavaScript 运行环境。理解这个环境的边界与数据上下文是编写健壮脚本的前提。

一个标准的扩展脚本基本骨架遵循以下规范:

  • 必须导出一个名为 main 的全局函数,其接收单一参数 config(代表当前的完整配置对象树)。
  • 函数内部可以直接使用常见的标准库函数(如 Array.prototype.filter、Array.prototype.map、RegExp、JSON 以及基础的数学运算工具)。出于桌面客户端沙箱安全性的考虑,脚本环境通常被严格隔离,禁止直接访问本地宿主机的文件系统(如 Node.js 的 fs 模块)或发起任意的网络请求(如 fetch 或 axios)。
  • 函数末尾必须通过 return config; 将修饰后的对象完整交还给客户端,否则客户端会由于收到空对象而拒绝更新并报错。

在编写脚本时,有三个最核心的协议字段需要重点掌控:

  • config["rules"]:存储分流规则的字符串数组。正如在分流规则匹配顺序与短路求值原则:避免代理规则被前置直连覆盖中所强调的,规则评估严格遵循短路求值原则,因此自定义的高优先级规则必须通过 unshift 方法插入到数组的最前端。
  • config["proxies"]:存储所有底层代理节点定义的对象数组。每个元素均包含节点的协议类型、服务器地址、端口、加密方式及节点名称等核心属性。
  • config["proxy-groups"]:存储所有策略组的定义数组。负责组织节点的调度逻辑(如故障转移 Fallback、自动延迟测速 Url-Test、手动选择 Select 等)。

三、实战案例:高频场景下的脚本编写

在实际网络调优中,最典型的三类脚本需求涵盖节点名称净化、独立策略组新建与专属分流规则注入:

场景一:利用正则表达式过滤与剔除垃圾节点

很多服务商提供的订阅中夹杂着大量无用的流量提示节点、过期失效节点或不符合个人偏好的地域节点。通过脚本可以在内存中直接将其清理:

function main(config) {
  // 定义需要排除的节点名称正则(如包含 官网、剩余流量、重置时间)
  const excludeRegex = /(官网|流量|重置|过期|提示|通知)/i;
  
  // 过滤 proxies 数组
  if (Array.isArray(config.proxies)) {
    config.proxies = config.proxies.filter(p => !excludeRegex.test(p.name));
  }
  
  // 同时从各个策略组的 proxies 列表中剔除已删除的节点名称
  if (Array.isArray(config["proxy-groups"])) {
    config["proxy-groups"].forEach(group => {
      if (Array.isArray(group.proxies)) {
        group.proxies = group.proxies.filter(name => !excludeRegex.test(name));
      }
    });
  }
  return config;
}

场景二:自动化无缝注入独立业务策略组与规则

假设我们需要为一个新兴的海外 AI 服务单独建立一个策略组,并让所有相关域名绝对走该策略组:

function main(config) {
  // 1. 确保规则数组存在
  if (!config.rules) config.rules = [];
  
  // 2. 将高优先级规则插入到规则列表最顶部
  const customRules = [
    "DOMAIN-SUFFIX,claude.ai,AI服务",
    "DOMAIN-SUFFIX,anthropic.com,AI服务",
    "DOMAIN-KEYWORD,openai,AI服务"
  ];
  config.rules.unshift(...customRules);
  
  // 3. 在策略组中追加专用的“AI服务”策略组
  if (Array.isArray(config["proxy-groups"])) {
    const aiGroup = {
      name: "AI服务",
      type: "select",
      proxies: ["节点选择", "DIRECT"]
    };
    // 将新建的策略组插入到策略组列表的前排
    config["proxy-groups"].unshift(aiGroup);
  }
  return config;
}

四、脚本调试、故障排错与安全防御

在扩展脚本编写与上线过程中,为防止语法逻辑错误导致内核启动失败或配置毁损,应当遵循以下实战排错原则:

第一步是善用客户端内置的“配置预览(Preview)”功能。在 Clash Verge Rev 的扩展配置管理界面保存脚本后,切勿急躁地直接重启核心,而是应当先右键点击配置条目选择“查看处理后的配置”。在弹出的实时代码视窗中,仔细核对生成的 YAML 文本:检查自定义的策略组名称是否与规则完全一致、目标规则是否确实准确无误地排列在了规则列表的最顶层,以及策略组内部引用的节点名称列表中是否存在拼写断裂。

第二步是严格做好空指针防御性编程。远程订阅的格式规范可能随时发生微小变化。例如,某些纯节点池订阅在拉取后可能压根没有提供 rules 字段,或者某些订阅的 proxy-groups 字段拼写为 proxy_groups。在编写 JavaScript 脚本时,对访问的每一个对象层级都要进行严格的防御性判断(例如使用可选链操作符 config?.rules?.unshift(...) 或明确的数组类型判断),防止因脚本在运行时抛出未捕获的运行时异常(Unhandled Exception)而导致整个客户端配置管线中断回退。

第三步是配合 Fake-IP与Redir-Host模式全方位深度对比 与 彻底排查与消除DNS泄漏:从系统解析器到WebRTC的完整加固 的架构准则,在脚本中统一覆盖注入高性能的 DNS 模块,将解析模型强行固定为 Fake-IP 并启用严格的本地 DNS 劫持机制,确保整套客户端环境兼具极其灵活的业务扩展性与磐石般的系统运行健壮性。


参考资料

NODE LADDER RESEARCH TEAM
网络协议、分流与测量方法编辑。来源和适用条件见正文;示例不代表本站实测。