链式代理原理
链式代理的原理如图所示:

从某种意义上说,图中所示的是一个三跳的链式代理(即三级链式代理),即代理服务器 1 做入口代理,代理服务器 2 做中转代理,代理服务器 3 做代理出口;从另一种意义上说这又是一种普通的二级链式代理,即代理服务器 1 当做运营商,代理服务器 2 做代理入口,代理服务器 3 做代理出口。
链式代理本质上还是代理中转的一种,只不过其像一串珠子一样挨个串在了一条链路上,在网络访问时依序执行。
Clash覆写
覆写是 Mihomo 家族(即 Clash 家族)比较进阶的一种玩法,分为 YAML 覆写和 Javascript 覆写两种,YAML 写的是类似订阅、遵循订阅同等规则的 YAML 文件,Javascript 覆写则是遵循标准的 Javascript,使用 Mihomo 的对应函数对原代理做覆写编辑相比于直接修改订阅文件,使用覆写可以避免在更新订阅时新的订阅文件覆盖旧的文件,这样就不需要每次更新完订阅都去订阅文件中重新修改了。
Javascript脚本
Javascript 脚本的核心是一个名为 config 的对象,config 对象由 config.proxies 数组、config["proxy-groups"] 数组和 config.rules 数组;从名字上看就可以知道他们的含义,即:config.proxies 数组是节点数组,包含订阅中的所有节点,config["proxy-groups"] 数组是订阅策略分组数组,config.rules 是分流规则数组。
在 Javascript 覆写脚本里,核心的用法主要有.includes 函数、 .filter 函数、.map 函数、.push 函数和.unshift 函数,以及 => 符号、proxy 对象(即结构体,proxy 对象表示代理节点)、proxy-groups 对象(策略组)和 rules(规路由规则)。
proxy对象
proxy 对象会根据其协议不同有所区别,但是他们有些通用属性,其结构大致如下:
{
name: "香港 01 | IPLC", // 节点名称(字符串)
type: "ss", // 协议类型(字符串),如 ss, vmess, trojan, vless
server: "123.45.67.89", // 服务器 IP 或 域名(字符串)
port: 443, // 端口(数字)
cipher: "aes-128-gcm", // 加密方式
password: "mypassword", // 密码
udp: true // 是否开启 UDP(布尔值)
}proxy-groups对象
proxy-groups 主要分两种,一种是选择式的策略组,还有一种是类似自动选择的每隔一段时间就会检查内部节点延迟情况的策略组。
选择式的策略组结构如下:
{
name: "策略组名称",
type: "select", // 或者是 url-test, fallback 等
proxies: ["节点A", "节点B", "DIRECT"] // 这是一个纯字符串数组
}自动选择式的策略组结构如下:
{
name: "自动选择",
type: "url-test",
"include-all": true,
"exclude-filter": EXCLUDE,
url: TEST_URL,
interval: 180,
tolerance: 25,
}其中,"include-all" 字段表示是否包含全部节点;"exclude-filter" 字段是用于排除一些名称的过滤器,比如某些机场带进来的一些无用节点(如套餐过期时间、套餐剩余用量之类的用于展示信息的无用节点),可以使用定义好的正则表达式;url 字段是用于测试节点延迟的链接,和 "exclude-filter" 字段一样可以使用提前定义好的变量;interval 字段表示测速的时间间隔,单位是秒;tolerance 字段表示容忍度或切换节点的阈值,假如测速后当前使用的节点延迟与最低延迟之间差距不超过阈值则自动选择的策略不会切换节点,这样有助于减少节点的切换频率,避免由于频繁切换节点造成的断线或者重连。
rules对象
rules 对象和 proxy 对象一样,一般在机场的订阅中就已经包含一些,往往是根据自己的需求做规则细分或者添加一些特殊的规则分流所用,所以一般也不需要完全推翻旧的规则集。如果需要推翻旧的规则集使用自己写的新规则集只需要下面的写法即可:
config.rules = [
新规则集
];=>符号
=> 符号比较好理解,主要是将左侧的接口传递到右侧去,待右侧执行完毕后将右侧的结果作为整个表达式的值。例:
(proxy) => proxy.name === "US家宽"这部分代码意为 proxy 接口的值传到左侧的 proxy.name 然后执行 proxy.name==="US 家宽" 这个判断,将判断的结果作为这个表达式的值。这部分有点类似 linux 中的 => 传递符号,即左侧的 proxy 仅仅作为传递的入口,从 => 符号传递至右侧 proxy.name 然后执行后续的操作。
.includes函数
.includes 函数是个检索函数,用于检查字符串里有没有某个词,返回值是包含该词的对应元素。
proxy.name.includes("US家宽")例如该代码表示返回名字中包含“US 家宽”的代理名,.includes 函数常常搭配.filter 函数或者.map 函数等使用。
.filter函数
.filter 函数主要是从数组中遍历每一个元素,为每个元素执行回调函数,若回调函数返回 true 则将该元素加入数组中。
config.proxies = config.proxies.filter(proxy => {
return !proxy.name.includes("流量") && !proxy.name.includes("官网");
});比如这部分代码,意味着只有不包含“流量”且不包含“官网”的节点才返回 true 被保留,这里就同时用到了 => 符号和.filter 函数,proxy 就是!proxy.name.includes("流量") && !proxy.name.includes("官网") 的判断主体,只要 proxy 不符合判断条件就会返回 false,返回的数组中就不会出现当前这个 proxy,反之数组中就会出现这个 proxy。
.map函数
.map 函数则是负责从对象中获取特定的数组返回。
const proxyNames = config.proxies.map(proxy => {
return proxy.name;
});这部分代码就意味着将 config.proxies 对象中的名称(即 proxy.name)返回,在这部分代码里就意味着将所有的 proxy 名字都返回了,返回的结果是一个数组,也就是名称数组。
.push函数和.unshift函数
.push 函数和.unshift 函数是一对功能类似作用相反的函数,.push 函数是向数组尾部追加元素,而.unshift 函数则是向数组的头部添加元素
通过这些符号和函数,就可以对上述对象进行绝大部分操作,完成绝大多数使用场景下的覆写任务。
Javascript脚本入口
Javascript 脚本需要一个入口函数,其结构比较简单,即:
function main(config) {
js脚本内容
}yaml脚本
相比 javascript,yaml 的语法结构就简单很多,yaml 更像是一种给人看的文本结构。yaml 易读的优点背后换来的则是极其低下的编写效率和痛苦的编写过程,如果是人工写覆写的话还是 javascript 更高效。
和 python 类似,yaml 对缩进有着严格的要求,可以说缩进就是 yaml 分割语句的根本。现在机场的订阅拉取后也都是 yaml 格式,他们的缩进也非常严格。具体的写法可以参考各大机场的订阅文件和下面的两个链接:
规则集编写
无论是 yaml 脚本还是 javascript 脚本的规则集编写都要遵循规则集编写的一些写法,主要是域名匹配一类的内容,clash verge 的文档在这方面十分详细,可以直接参考学习:
链式代理
讲完了 Clash 覆写就该看看链式代理了,同样的,链式代理的覆写也分别可以用 javascript 脚本和 yaml 脚本两种方式编写。
链式代理的javascript脚本
在一些旧版的 clash 内核里尚且支持 relay,但是现在新版的 mihomo 内核均不支持 relay 的方式组织链式代理,现在的链式代理使用 dialer-proxy 的方式组织链式代理,这里也仅讲述 dialer-proxy 的方式写链式代理。
function main(config) {
if (!config.proxies) { config.proxies = []; }
// 1. 检查是否已经存在
if (!config.proxies.some((p) => p.name === "US家宽")) {
// 2. 插入节点
config.proxies.unshift({
name: "US家宽",
type: "socks5",
server: "123.45.67.89", // 👈 填入你的 Socks5 IP
port: 1080, // 👈 填入你的端口数字
username: "myuser", // 👈 填入用户名
password: "mypassword", // 👈 填入密码
udp: true,
"dialer-proxy": "自动选择", // 👈 确保你的策略组里有叫“自动选择”的组
});
}
return config;
}这就是一个典型的链式代理的示例,这里用到的.some 函数表示如果 config.proxies 中有任何一个的名字(这部分来自于 (p)=>p.name==="US 家宽")为 US 家宽则返回 true,搭配前面的! 符号,组成如果这个组别中没有 US 家宽这个名字,那么就执行下面大括号的内容。
而大括号的内容则是一个经典的插入节点的操作,confg.proxies.unshift 表示在这些节点的最前面塞入下面的节点,这个节点内容包含 name、type 等元素。这个节点则是一个标准的 socks5 节点的元素写法,一般从各大服务商买的家宽 IP 都是这种 IP+port+username+password 的形式,我平常使用的家宽是 webshare
之后就要设置允许在链式代理中使用 udp 流量(即 udp:true 字段),然后在 "dialer-proxy" 字段中选择链式代理入口的组名或者节点名,这里我一般推荐使用自动选择这种组作为链式代理的入口,可以有效避免因为设置的链式代理入口不可达而导致整个链式代理不可用。
链式代理的yaml脚本
yaml 脚本相比之下依然是简单易读:
proxies:
- name: "US家宽"
type: socks5
server: "123.45.67.89" # 👈 填入你外买的 Socks5 IP
port: 1080 # 👈 填入端口(纯数字)
username: "myuser" # 👈 填入用户名
password: "mypassword" # 👈 填入密码
udp: true
"dialer-proxy": "自动选择" # 🌟 核心:直接在这里指定前置代理策略组但是这种写法效率比较低下,虽然在添加代理节点这种情况并没有比 javascript 简单,但是在将节点添加到策略组的时候就会比 javascript 麻烦,策略组越多,这一过程越繁琐。
默认评论
Halo系统提供的评论