打开 Clash Verge Rev 源码,我发现了一些有意思的东西

🤔 我为什么决定读源码

一开始用 Clash Verge Rev,我只是把它当成一个工具:点开、切换节点、关上。但用久了总会遇到些稀奇古怪的问题,比如某个订阅更新失败、TUN 模式在某些机器上异常断网、或者是想调一个界面上没有的参数。翻文档、搜 Issue,有时候能解决,有时候就卡住了。后来我想,与其每次像拆盲盒一样猜,不如直接看代码——它到底是怎么处理这些逻辑的。

于是我开始啃源码。在这之前我看过一些 Rust 和 React,但都不算精通。所以我读源码的目的不是要提交 PR,而是想搞清楚:这个软件到底在背后做了什么。如果你也对底层的实现感兴趣,或者想自己改一点小功能,这篇文章应该能给你一个起点。当然,如果你连配置都还没弄明白,建议先看完 配置教程 再来,那样读代码会更有感觉。

🛠️ 读源码前的准备工作

读一个大项目的源码,最忌讳一上来就打开文件乱翻。Clash Verge Rev 的代码仓库分了好几个子项目,而且前端和后端用了完全不同的技术栈。我自己的准备步骤是这样的:

  • Clone 仓库: git clone https://github.com/Clashforwinodws/clash-verge-rev-download(当然,实际地址以官方为主)。
  • 安装 Node.js 和 pnpm: 前端 React 项目用 pnpm 管理依赖。
  • 安装 Rust 工具链: 后端用 Rust,Tauri 也是 Rust 写的,需要 rustupcargo
  • 选一个顺手的 IDE: 我用 VS Code,装了 Rust Analyzer 和 Tauri 插件,看代码跳转非常方便。

还有一点:读代码的时候不要想着一次全部看懂。Clash Verge Rev 的代码量不小,而且和 Mihomo 内核有交互,但大部分交互都是通过 API 和进程管理完成的,并不需要你去读 Mihomo 的源码(当然读了更好)。你可以先关注一个具体的问题,比如“订阅更新是怎么触发的”,然后顺着这条线去追代码,会高效很多。

📁 代码结构总览:前端、后端、内核各司其职

打开仓库你会看到几个主要的目录:src 下面是前端 React 代码,src-tauri 下面是 Rust 后端代码。前端负责界面展示和用户交互,后端负责系统级操作(比如读写配置文件、管理内核进程、处理系统托盘等)。这两部分通过 Tauri 的 IPC 机制通信,前端调用后端的命令,后端返回结果。

Mihomo 内核是一个独立的二进制文件,Clash Verge Rev 会根据平台下载对应的内核,然后在客户端启动时运行它。内核负责真正的代理功能:节点连接、规则匹配、DNS 处理。客户端和内核之间通过 HTTP API 交互,客户端发送配置和指令,内核返回日志和状态。如果你已经了解过 Mihomo 内核,这一层理解起来会非常自然。

组件技术栈职责
前端React + TypeScript + Vite用户界面、配置编辑、节点展示、连接面板等
后端Rust + Tauri文件读写、进程管理、系统托盘、自动更新、权限控制
内核Rust (Mihomo/Clash Meta)代理引擎、规则引擎、DNS 解析、TUN 模式

这个分层设计的好处是:你可以单独修改前端界面而不需要重新编译 Rust,或者反过来修改后端逻辑而不影响前端。很多用户利用这一点,自己 Fork 一份代码,只改界面样式或者添加一个自定义按钮。

⚛️ 前端部分:React 组件与配置管理的入口

前端代码的主体在 src 目录下,使用了 React 的函数组件和 Hooks。如果你是 Web 开发者,会觉得很亲切。我一开始是从 src/pages 目录入手的,里面每个文件对应一个页面:Proxies.tsx 是节点页面,Configs.tsx 是配置页面,Settings.tsx 是设置页面。找到你想看的功能,直接打开对应文件,大多数逻辑都能看懂。

前端和后端通信的核心是 src/utils/backend.ts 或类似文件。它封装了所有对后端命令的调用,比如 getProxies()switchProxy()updateConfig()。当你在界面上点击一个按钮,最终都会调用这些封装好的函数。如果你想修改某个按钮的行为,可以在这个文件里找到对应的调用,然后顺藤摸瓜到后端 Rust 的实现。

有一个小细节:前端的配置编辑页面(ConfigEditor)并不是简单的文本框,它用了一个支持 YAML 高亮和语法检查的编辑器组件。当你修改配置并点击“保存”时,前端会把整个 YAML 字符串通过 IPC 发给后端,后端再写入文件并通知内核重载。这个过程中,如果 YAML 语法有错,后端会返回错误信息,前端再展示红框提示。理解这个流程后,你就能明白为什么配置文件改错时,前端能准确地告诉你错在哪一行了。

🦀 后端部分:Tauri 命令与 Rust 核心逻辑

后端的入口在 src-tauri/src/main.rs,从这里开始,你可以看到 Tauri 应用是如何初始化的。所有暴露给前端的命令都在 src-tauri/src/commands 目录下(或者类似路径),每个命令都有对应的处理函数。比如 get_proxies 命令会调用内核的 /proxies API,然后解析 JSON 返回给前端。

我重点读了两个模块:配置管理内核进程管理。配置管理模块负责配置文件的读取、保存、导入导出,以及订阅的定时更新。你会看到里面大量使用 tokio 的异步操作来处理文件 IO 和网络请求。订阅更新的逻辑其实就是一个定时器加上 HTTP 客户端,然后调用配置保存函数。如果你看过 订阅更新时间 那篇文章,在代码里可以找到对应的实现细节,比如 interval 是分钟为单位、更新失败后的重试策略等。

内核进程管理模块则负责下载 Mihomo 内核二进制、启动进程、监控进程退出。你会在代码里看到针对不同操作系统的条件编译,比如 Windows 下用 std::process::Command 启动 clash-verge-service.exe 来获得管理员权限,而 Linux 下则直接运行内核并设置环境变量。这个模块还处理了内核的自动更新,类似于客户端自身的更新逻辑。

🔗 与 Mihomo 内核的交互:下载、启动、通信

Clash Verge Rev 和 Mihomo 内核的交互是通过 HTTP API 完成的。内核默认监听在 127.0.0.1:9090,客户端通过这个地址发送 REST 请求来获取节点信息、切换节点、重载配置。在 Rust 后端里,这些请求被封装在一个 HTTP 客户端模块中,你可以看到每个 API 对应的 URL 和参数。如果你对 API 本身感兴趣,可以翻一下 冷门功能 那篇,里面提到过外部控制 API 的用法。

内核的启动参数也很有讲究。比如在 Windows 上启用 TUN 模式时,客户端会传递 -tun 和相关的 WFP 参数给内核。这些参数会直接影响到 TUN 模式 的行为。如果你在代码里搜 tun,会发现所有和虚拟网卡相关的配置项都被翻译成内核启动参数或 API 调用。这也是为什么内核版本和客户端版本需要匹配——如果 API 接口或参数变了,旧客户端可能就无法正确控制新内核。

有一个很实用的细节:客户端会定期向内核请求连接列表,用于显示连接面板。这个请求的频率和更新机制在代码里可以找到,通常是在连接面板打开时才会启动轮询,关闭后停止,这样可以节省资源。如果你觉得连接面板刷新太慢,可以在这里调整轮询间隔。

🐛 调试技巧:改代码看效果的正确姿势

读代码是一回事,改代码是另一回事。如果你想自己改 Clash Verge Rev 的源码,下面这几个调试技巧可能对你有帮助:

  • 前端调试: 直接运行 pnpm dev 启动 Vite 开发服务器,前端可以热更新,你改 React 代码后浏览器或客户端窗口会实时刷新。注意这样运行的是前端开发模式,后端可以单独运行 cargo tauri dev 一起启动。
  • 后端调试: 在 Rust 代码里用 println!log::info! 输出调试信息。运行 cargo tauri dev 时这些日志会直接输出到终端。
  • 查看内核日志: 内核的日志可以通过客户端的“日志”页面查看,也可以在启动内核时设置环境变量 RUST_LOG=debug 来获取更详细的信息。
  • 使用 tauri.conf.json 配置开发模式: 这个文件在 src-tauri 目录下,你可以修改端口、窗口标题等。

一个常见的调试场景是:你想修改订阅更新的默认 interval。你可以在后端代码中找到处理订阅更新的函数,把默认值改掉,然后重新运行。修改后可以直接在设置界面看到效果。这种“改一点看一点”的方式比从头读代码快得多。

🔧 实际案例:给客户端加一个自定义开关

为了验证我理解了代码,我给自己定了个小目标:加一个“一键清空所有延迟测试结果”的按钮。虽然功能很小,但涉及到了前端按钮、后端命令、内核 API 三个部分。

首先在前端的设置页面(Settings.tsx)加一个按钮,然后在前端的后端封装文件里加一个 clearDelayCache() 函数。接着在后端 Rust 代码里实现一个 clear_delay_cache 命令,这个命令调用内核的 /group/proxies 相关 API 来清除缓存。最后在内核的 API 文档里找到了对应的端点,测试通过。

整个过程花了我一个下午,但收获巨大。不仅理解了整个数据流,还顺带看懂了 Tauri 命令的注册方式。如果你也想尝试类似的小改动,建议从最简单的开始:改一个文本颜色、加一个静态提示,然后逐步深入。官方没有提供完整的开发文档,但代码结构本身很清晰,配合 GitHub 仓库 的 README 和 Wiki,足以让你上手。

💡 读源码之后的一些思考

读完源码后我最大的感受是:很多看似复杂的问题,在代码层面其实有非常直白的解释。比如之前一直困扰我的“为什么有时候代理规则不生效”,看了代码才知道规则匹配是有顺序的,而且有些规则是内核在启动时一次性加载到内存里的,修改后必须重载才能生效。这些细节在文档里往往不会强调,但代码里一目了然。

另一个感受是,Clash Verge Rev 的架构设计相当克制。它没有把所有功能都塞进客户端,而是把核心的代理逻辑留给了 Mihomo 内核,客户端只管配置和展示。这种分层让代码更容易维护,也让用户可以根据需要选择不同的内核版本。如果你对内核的扩展感兴趣,可以看 Mihomo 插件开发 那篇,里面讲了如何自己写一个插件并挂载到内核上。

最后,如果你也在读 Clash Verge Rev 的源码,欢迎交流。读代码的过程难免会有些孤独,但每次搞清楚一个逻辑后的那种豁然开朗,是看文档体会不到的。遇到不懂的,可以在 常见问题 或者社区 Issue 里找找,说不定已经有人讨论过。

📖 继续阅读