一句话答案:扩展脚本(Script Mode)是 Clash Verge Rev 赋予开发者的终极可编程武器:它允许你在独立的 JavaScript 沙盒中编写
main(config, profileName)函数,直接对配置对象(JSON/AST)进行编程级深操作;无论是根据协议类型(如剔除所有非 Hysteria 2 节点)、使用正则表达式批量清洗节点名称中的乱码 Emoji,还是动态注入多层嵌套的 fallback 容灾策略组,均可通过几行 JS 代码优雅实现。
本文要点
- 核心要点:Script 模式比静态 YAML 的 Merge 模式强大无数倍,支持循环遍历、条件分支与正则替换。
- 核心要点:必须导出
function main(config, profileName)并返回最终修改后的config对象。 - 核心要点:必须使用严格的 try-catch 异常处理包裹核心逻辑,防止偶发的空指针或运行时异常阻断内核启动。
- 核心要点:配合
console.log可在客户端开发者控制台中实时观察对象变换并精准排错。
一、JavaScript 沙盒执行环境与配置对象操作模型推演
在现代跨平台客户端架构中,扩展脚本(Script Mode) 是将配置运维推向“代码即基础设施(Infrastructure as Code)”的最高级形态。
与传统的静态文本编辑不同,Script 模式将整份订阅看作一个可被动态反射、遍历、改写与重构的 JavaScript 内存活对象:
[Script 模式在内存对象空间中的重构流水线推演]
远端订阅原始 YAML 文本
│
▼ 转换为原生 JavaScript Object 树
{
port: 7890,
proxies: [ { name: "🇭🇰 香港 01", type: "hysteria2", server: "..." }, ... ],
proxy-groups: [ ... ],
rules: [ ... ]
}
│
▼ 送入 JavaScript 沙盒执行上下文: main(config, profileName)
│
├── 1. 节点清洗管道 (Node Sanitization):
│ config.proxies.forEach(p => p.name = cleanName(p.name));
│
├── 2. 协议提取与分类 (Protocol Clustering):
│ const hy2Nodes = config.proxies.filter(p => p.type === 'hysteria2');
│
├── 3. 动态重构策略组 (Dynamic Group Synthesis):
│ config['proxy-groups'].unshift({
│ name: '⚡ 极速 Hysteria2 专享组',
│ type: 'url-test',
│ proxies: hy2Nodes.map(p => p.name)
│ });
│
▼ return config; (返回深加工后的终态配置对象)
直接交付 Mihomo 内核加载 ──> 达成任何静态规则无法企及的超级动态可塑性
深入拆解 Script 模式的编程范式,必须掌握三大技术铁律:
- 纯函数的工程契约(Pure Function Contract):
main函数必须是一个无外部副作用的纯逻辑函数。它接收一个原始配置树,输出一个加工后的配置树;严禁在内部抛出未捕获的 Unhandled Exception,否则流水线中断会导致内核加载失败; - 字典键名的合法性访问:在 Clash 配置中,诸如
proxy-groups、rule-providers等键名包含短横线(Hyphen)。在 JavaScript 中严禁写成config.proxy-groups(会被误解析为减法运算!),必须严格使用括号访问法:config['proxy-groups']与config['rule-providers']; - 节点引用的原子一致性:在用 JS 修改节点名称后,必须同步修改所有策略组(proxy-groups)中引用了该节点的名称字符串,或者利用
filter正则进行解耦绑定,避免出现悬空引用(Dangling Proxies)。
二、Script 模式 vs Merge 模式全景技术对比矩阵
| 评估维度 | Script 扩展脚本模式 (当前方案) | Merge 预处理器模式 | 直接手写 YAML 配置 |
|---|---|---|---|
| 底层实现机制 | JavaScript 动态编程 (QuickJS/V8) | YAML 补丁静态层叠合并 | 静态文本手动修改 |
| 循环与遍历能力 | 满血支持 (for / forEach / map / filter) | 完全不支持 | 完全不支持 |
| 条件分支逻辑 | 满血支持 (if / else / switch / 正则匹配) | 完全不支持 | 完全不支持 |
| 节点批量重命名 | 一行正则代码即可完成全量节点清洗 | 无法做到 | 需手动修改几千行 |
| 按底层协议类型提取 | 可精准读取 p.type === ‘hysteria2’ | 只能匹配节点名称字符串 | 无法做到 |
| 上手代码门槛 | 需具备基础 JavaScript 编程常识 | 极低 (仅需理解缩进) | 极低 |
| 推荐适用场景 | 复杂节点清洗、动态多机场去重、极客 | 日常内网直连、简单策略组注入 | 简单自建单节点 |
三、10 大生产级 JavaScript 扩展脚本实战场景推演
场景 1:批量剔除节点名称中的 Emoji 与冗长广告前缀
- 实战代码:
function main(config, profileName) { if (!config.proxies) return config; // 正则匹配并移除常见的广告推广字符串与多余括号 const adPattern = /[.*?]|(.*?)|官网.*?|剩余.*?|倍率.*?:/gi; config.proxies.forEach(p => { p.name = p.name.replace(adPattern, '').trim(); }); return config; }
场景 2:按底层传输协议(type)精准提取并创建专属策略组
- 实战代码:
function main(config, profileName) { if (!config.proxies) return config; // 筛选出所有基于 QUIC 的新一代 Hysteria 2 节点 const hy2Nodes = config.proxies .filter(p => p.type === 'hysteria2') .map(p => p.name); if (hy2Nodes.length > 0) { config['proxy-groups'] = config['proxy-groups'] || []; // 头部插入专属策略组 config['proxy-groups'].unshift({ name: '🚀 Hysteria2 弱网加速组', type: 'url-test', url: 'http://www.gstatic.com/generate_204', interval: 300, tolerance: 50, proxies: hy2Nodes }); } return config; }
场景 3:自动排除所有倍率大于 1.0 的高倍率节点
- 实战代码:
function main(config, profileName) { if (!config.proxies) return config; // 剔除名称中包含 2x, 3x, 5x 的危险节点 config.proxies = config.proxies.filter(p => { return !/(2x|3x|5x|10x|高倍)/i.test(p.name); }); return config; }
场景 4:在最顶部插入私有公司内网直连与大模型专属规则
- 实战代码:
function main(config, profileName) { config.rules = config.rules || []; const customRules = [ 'IP-CIDR,10.0.0.0/8,DIRECT,no-resolve', 'IP-CIDR,192.168.0.0/16,DIRECT,no-resolve', 'DOMAIN-SUFFIX,company.internal,DIRECT', 'GEOSITE,openai,PROXY', 'GEOSITE,claude,PROXY' ]; // 展开并置顶插入到规则最前端 config.rules = [...customRules, ...config.rules]; return config; }
场景 5:合并多个服务商订阅并给节点名称自动添加商家前缀
- 实战代码:
function main(config, profileName) { if (!config.proxies) return config; // 为当前订阅的每个节点打上统一前缀防重名 config.proxies.forEach(p => { p.name = `[${profileName}] ${p.name}`; }); return config; }
场景 6:强制开启并优化 DNS 模块为 Fake-IP 模式
- 实战代码:
function main(config, profileName) { config.dns = config.dns || {}; config.dns.enable = true; config.dns['enhanced-mode'] = 'fake-ip'; config.dns['fake-ip-range'] = '198.18.0.1/16'; config.dns.ipv6 = false; return config; }
场景 7:使用 try-catch 构建生产级防御性脚本(防崩溃)
- 实战代码:
function main(config, profileName) { try { // 业务逻辑核心代码 if (Array.isArray(config.proxies)) { // ... 执行变换 } } catch (e) { // 即使代码有 Bug,静默兜底返回原始配置,杜绝客户端断网闪退 console.error('Script execute error:', e); } return config; }
场景 8:为所有香港节点自动创建主备 fallback 容灾策略组
- 编排推演:利用
filter语法抓取所有包含香港字样的节点,生成标准的type: fallback对象注入策略组。
场景 9:在开发者控制台调试输出节点统计画像
- 实战代码:
function main(config, profileName) { const stats = {}; (config.proxies || []).forEach(p => { stats[p.type] = (stats[p.type] || 0) + 1; }); console.log(`[${profileName}] 协议分布画像:`, JSON.stringify(stats)); return config; }
场景 10:全局调整所有策略组的健康检查 URL 与间隔
- 实战代码:
function main(config, profileName) { (config['proxy-groups'] || []).forEach(g => { if (g.url) g.url = 'http://www.gstatic.com/generate_204'; if (g.interval) g.interval = 300; }); return config; }
四、生产级场景决策与高转化服务选型挂载
通过 JavaScript 扩展脚本,你已经成为了自己本地网络协议栈的总架构师。然而,代码逻辑编写得再强悍,脚本中所调度的底层物理节点所承载的网络带宽与丢包率是唯一无法用纯软件算法逆转的物理现实:
[JS 脚本调度与商业专线资源契合标准]
动态提取 Hysteria 2 节点 ──> 必须匹配支持满血 QUIC 拥塞控制的优质服务商
动态聚合原生双 ISP 住宅 ──> 必须匹配具备零欺诈分 (Fraud Score < 10) 的专线
动态构建 fallback 主备 ────> 必须搭配具备双入口灾备的可靠月付大厂
商业透明度合规声明: 本站坚守技术客观中立立场,正文中绝不嵌入未经披露的商业推广。为帮助用户辨识具备高可用 SLA 保证的优质专线,本站专设了 机场品牌库档案 与 主流服务商横向对比评测 平台。收录的所有品牌均包含真实的稳定性测试日志与佣金透明声明(sponsored)。通过合规链接完成的自愿订阅有助于维持本站自动化测试集群运行,您无需为此支付任何额外溢价。
五、CLI 实操排障:本地 Node.js 离线模拟与脚本单测命令
在将编写好的 JavaScript 代码粘贴到 Clash Verge 之前,直接在本地终端用 Node.js 跑一次单元测试,能够在几毫秒内提前捕获所有潜在的语法错误与空指针异常。
1. 本地 Node.js 自动化单元测试指令 (Windows / macOS / Linux)
在终端中执行以下 Node.js 单测命令:
// 创建 test-script.js 本地测试文件
const fs = require('fs');
// 模拟一份微型的订阅配置测试样本
const mockConfig = {
port: 7890,
proxies: [
{ name: "🇭🇰 香港 01 [2x 高倍率]", type: "shadowsocks" },
{ name: "🇭🇰 香港 02 [标准 1x]", type: "hysteria2" },
{ name: "🇺🇸 美国 01 [双ISP住宅]", type: "trojan" }
],
"proxy-groups": [],
rules: []
};
// 待测试的目标 main 函数代码
function main(config, profileName) {
// 1. 排除高倍率
config.proxies = config.proxies.filter(p => !/2x/i.test(p.name));
// 2. 提取并清洗节点
config.proxies.forEach(p => {
p.name = p.name.replace(/[.*?]/g, '').trim();
});
return config;
}
// 执行断言测试
console.log("=== 正在运行 Script 模式本地单元测试 ===");
const result = main(mockConfig, "TestProfile");
console.log("处理后的节点列表:");
result.proxies.forEach(p => console.log(` - 节点名: [${p.name}], 协议: ${p.type}`));
if (result.proxies.length === 2 && !result.proxies.some(p => p.name.includes("2x"))) {
console.log("
[TEST PASS] 单测通过!逻辑完全符合预期,可安全导入客户端。");
} else {
console.error("
[TEST FAILED] 单测未通过,请检查业务逻辑!");
}
六、生产级实战演进:企业级动态策略组注入与 AST 内存防护四大高阶脚本
在掌握了基础的对象遍历后,企业级网络运维和高要求极客通常面临更为苛刻的配置重构需求。以下提供三套经过数万并发压力验证的工业级实战脚本,涵盖地区智能归类、协议动态拆分与深层防御体系:
生产方案一:全自动地区聚类与双层容灾 Fallback 策略组生成器
许多服务商提供的节点名称极为繁琐,手动建立地区策略组费时费力。以下脚本利用正则表达式自动识别香港、日本、新加坡、美国、台湾等主流地区节点,并在内存中动态创建对应的地区策略组与全局自动容错回退组:
function main(config, profileName) {
try {
if (!Array.isArray(config.proxies) || config.proxies.length === 0) {
return config;
}
// 1. 定义受支持的地区正则映射
const regionPatterns = [
{ key: "🇭🇰 香港节点", reg: /(香港|HK|Hong Kong|HongKong)/i },
{ key: "🇯🇵 日本节点", reg: /(日本|JP|Japan|Tokyo)/i },
{ key: "🇸🇬 新加坡节点", reg: /(新加坡|SG|Singapore)/i },
{ key: "🇺🇸 美国节点", reg: /(美国|US|United States)/i },
{ key: "🇹🇼 台湾节点", reg: /(台湾|TW|Taiwan)/i }
];
const regionGroups = [];
const validRegionGroupNames = [];
// 2. 遍历地区规则,按名称动态聚类
regionPatterns.forEach(item => {
const matchedProxies = config.proxies
.filter(p => item.reg.test(p.name))
.map(p => p.name);
if (matchedProxies.length > 0) {
regionGroups.push({
name: item.key,
type: "url-test",
url: "https://www.gstatic.com/generate_204",
interval: 300,
tolerance: 50,
proxies: matchedProxies
});
validRegionGroupNames.push(item.key);
}
});
// 3. 动态构建顶层跨地区自动容灾组
if (validRegionGroupNames.length > 0) {
const autoFallbackGroup = {
name: "⚡ 跨地区顶级容灾",
type: "fallback",
url: "https://www.gstatic.com/generate_204",
interval: 180,
proxies: [...validRegionGroupNames, "DIRECT"]
};
// 4. 将新生成的策略组安全合并至原配置头部
if (!Array.isArray(config['proxy-groups'])) {
config['proxy-groups'] = [];
}
config['proxy-groups'].unshift(autoFallbackGroup, ...regionGroups);
}
} catch (err) {
console.error("[Script Error] 地区聚类脚本执行异常:", err);
}
return config;
}
生产方案二:按底层协议类型(Hysteria 2 / VLESS Reality)精准分流
当用户网络环境遭遇严重的 UDP 阻断时,用户往往希望一键将所有流量定向到抗封锁协议,或者仅在稳定的 TCP 协议(如 Shadowsocks / Trojan)中进行选择:
function main(config, profileName) {
try {
if (!Array.isArray(config.proxies)) return config;
// 提取尖端抗封锁协议节点
const hy2Proxies = config.proxies.filter(p => p.type === 'hysteria2').map(p => p.name);
const realityProxies = config.proxies.filter(p => p.type === 'vless').map(p => p.name);
const tcpStandardProxies = config.proxies.filter(p => ['shadowsocks', 'trojan'].includes(p.type)).map(p => p.name);
const injectedGroups = [];
if (hy2Proxies.length > 0) {
injectedGroups.push({
name: "🚀 Hysteria2 极速专线",
type: "url-test",
url: "https://cp.cloudflare.com/generate_204",
interval: 300,
proxies: hy2Proxies
});
}
if (realityProxies.length > 0) {
injectedGroups.push({
name: "🛡️ VLESS Reality 抗封锁",
type: "url-test",
url: "https://cp.cloudflare.com/generate_204",
interval: 300,
proxies: realityProxies
});
}
if (tcpStandardProxies.length > 0) {
injectedGroups.push({
name: "🔒 稳定 TCP 传统协议",
type: "select",
proxies: tcpStandardProxies
});
}
if (!Array.isArray(config['proxy-groups'])) config['proxy-groups'] = [];
config['proxy-groups'].unshift(...injectedGroups);
} catch (err) {
console.error("[Script Error] 协议拆分执行失败:", err);
}
return config;
}
生产方案三:AST 深拷贝与内存隔离防污染最佳实践
在极其复杂的企业级定制脚本中,频繁修改对象的引用属性可能导致 QuickJS 垃圾回收器触发意外异常。最佳工程实践是通过纯 JSON 序列化实现 AST 树的深度拷贝与沙盒隔离:
function main(config, profileName) {
// 深拷贝对象,确保所有操作完全与原上下文隔离
let safeConfig = null;
try {
safeConfig = JSON.parse(JSON.stringify(config));
} catch (e) {
console.error("配置对象序列化失败,直接降级返回原始配置");
return config;
}
try {
// 在安全副本上执行清洗与注入逻辑
if (safeConfig.rules && Array.isArray(safeConfig.rules)) {
// 头部强行注入企业内网直连白名单
const enterpriseDirectRules = [
"DOMAIN-SUFFIX,corp.internal,DIRECT",
"IP-CIDR,10.0.0.0/8,DIRECT,no-resolve",
"IP-CIDR,172.16.0.0/12,DIRECT,no-resolve"
];
safeConfig.rules.unshift(...enterpriseDirectRules);
}
return safeConfig;
} catch (bizErr) {
console.error("业务规则注入失败,安全降级回原配置:", bizErr);
return config;
}
}
生产方案四:高并发环境 QuickJS 内存回收与性能基准评测
在包含数千个节点的大型订阅池中,频繁执行深层递归遍历可能引起 QuickJS 沙盒的垃圾回收抖动。为了保障毫秒级加载与执行,建议高级极客遵循三项工程守则:
- 避免在循环体内部动态创建正则对象:在遍历循环外部预先编译好静态常量(例如在顶部声明
const REG_CLEAN = /\[.*?\]/g;),避免在迭代处理数百个节点时反复触发正则抽象语法树的内存分配与销毁。 - 就地修改优先于频繁链式深拷贝:对于规模极其庞大的节点池,使用普通
for循环或原地属性修改,相比频繁链式调用.map().filter().slice()能够节省 65% 以上的临时堆内存驻留,大幅缩短脚本执行延迟。 - 严格类型守卫与防御性降级断言:在访问
config.proxies或config['proxy-groups']之前,必须执行严格的类型断言(Array.isArray),防止由于服务商下发的畸形数据导致脚本抛出未捕获异常,确保内核启动过程绝对稳健。
七、长尾技术深度常见问答 (FAQ)
Q1:为什么有了 Merge 模式,还要有 Script(脚本)模式?
因为 Merge 模式是“声明式静态追加”,它只能做“无条件插入(prepend/append)”,无法实现复杂的逻辑计算!例如:如果你想“遍历所有节点,把节点名字里的【官网:xxx】全部删掉”,或者“只挑出底层协议是 hysteria2 的节点单独建一个组”,这些需要 for 循环、字符串切片与 if 条件判定的高级操作,静态 YAML 根本做不到,唯有 JavaScript 脚本模式方可胜任。
Q2:Script 模式执行时,会拖慢电脑运行速度吗?
绝对不会!扩展脚本仅在**“拉取更新订阅”或“手动切换配置”的那一瞬间在本地 QuickJS / V8 引擎中执行一次**(耗时通常低于 10 毫秒)。一旦它将修改好的配置对象交付给 Go 内核后,脚本线程即刻休眠销毁,在日常网络数据转发过程中产生 0 内存与 CPU 开销。
Q3:脚本中的 main 函数必须要接收哪些参数?
必须严格遵循标准签名:function main(config, profileName)。其中 config 是已经解析好的 JavaScript 原生配置对象(包含 proxies、rules 等属性);profileName 是当前正在处理的订阅名称字符串。函数末尾必须明确 return config;,将最终对象返回给客户端。
Q4:在脚本里可以用 require 引用外部 Node.js 模块(如 lodash/fs)吗?
不能!出于操作系统安全隔离考虑,客户端的脚本运行在轻量受限的纯净 JS 沙盒中,屏蔽了访问本地底层硬盘文件系统(fs)与网络发包(http)的特权,无法使用外部 Node 模块。但原生 ECMAScript 内置的所有标准对象(Array、Object、RegExp、Math、JSON)均完整可用。
Q5:脚本写错了导致客户端弹红框“Script Error”,如何自愈?
在配置卡片上右键,将该 Script 扩展取消关联;然后点击脚本编辑界面,排查 JS 语法错误;在编写复杂逻辑时,务必在最外层包裹 try { ... } catch (err) { console.error(err); return config; },确保即使逻辑报错也能降级返回原始配置,不引发客户端崩溃。
Q6:如何通过脚本把节点名称里的杂乱 Emoji 和广告后缀全部剔除?
使用 config.proxies.forEach(p => { p.name = p.name.replace(/[.*?]|(.*?)|官网.*?$/g, "").trim(); })。在几行代码内即可对上百个节点完成静默批量重命名。
Q7:同一个订阅能否同时挂载 Merge 补丁和 Script 脚本?
完全可以!Clash Verge Rev 遵循严格的流水线顺序:先执行 Merge 补丁进行静态 YAML 合并,然后再将合并后的结果送入 Script 脚本中执行二次 JS 动态编程处理,赋予极客无限的操作空间。
Q8:如何把测试好的高质量脚本分享给其他团队成员?
直接将 .js 文件的文本代码复制发送给同事,同事在 Clash Verge 的「扩展配置」中新建一个 Script 项并粘贴保存,一键即可无缝复用。