从源码构建和改客户端

这一页说明界面、桌面应用和 Mihomo 内核怎么分工,以及本地要装什么、怎么构建、前后端怎么通信、控制接口在哪,还有提交代码前要做的检查。

  • 客户端Tauri 2 + Rust + React
  • 内核Mihomo(Clash Meta)
  • 协议GPL-3.0

界面、桌面层和内核

客户端分三层。React 和 TypeScript 负责界面。Rust 和 Tauri 负责窗口、托盘和系统集成。Mihomo 负责解析配置、匹配规则、DNS、代理组和转发。查问题前先判断它落在哪一层。

层 技术 负责什么
界面层 React + TypeScript + Vite 展示配置、节点、代理组、规则、连接和日志,并处理界面上的操作
应用层 Tauri 2 + Rust 窗口、托盘、Mihomo 进程、系统代理、TUN、读写配置,以及和系统的集成
内核层 Mihomo(Clash Meta) 解析配置、处理协议、匹配规则、DNS、代理组、转发,以及对外的控制接口

说明 界面负责交互,Tauri 和 Rust 连接系统和本地文件,Mihomo 负责代理与路由。客户端通过内核的控制接口读写运行状态。出了问题,先分清是界面、桌面层,还是内核。

本地要先准备这些

需要 Node.js、Corepack 或 pnpm、Rust,以及当前系统要求的 Tauri 依赖。三个系统要装的系统库不一样。

系统 需要安装 备注
通用 Node.js、Corepack/pnpm、Rust 工具链 先执行 corepack enable,并确认 node、pnpm 与 cargo 都能用
Windows MSVC Rust 工具链、GNU patch;Windows ARM 还需要 LLVM/clang 确认 Rust 和 Node.js 已在 PATH 里。Windows ARM 还要满足 clang 的要求
macOS Xcode Command Line Tools xcode-select --install
Linux WebKitGTK 4.1、Ayatana AppIndicator、librsvg、patchelf 等开发包 包名随发行版变化,按 Tauri 文档里的依赖清单安装

注意 Linux 上的包名会变。当前贡献文档用 Ubuntu 举例,列出了 WebKitGTK、Ayatana AppIndicator、librsvg 和 patchelf。Windows ARM 还需要 LLVM 或 clang。以仓库里最新的 CONTRIBUTING.md 为准。

从拉代码到跑起来

先克隆仓库并安装依赖,再用 prebuild 准备 Mihomo 和运行所需的文件,然后进开发模式,或打出当前系统的安装包。

  1. 克隆仓库

    克隆之后进入项目目录。

  2. 安装前端依赖

    仓库用 pnpm。换成 npm 或 yarn,锁文件很容易冲突。

  3. 准备内核和运行文件

    运行项目的 prebuild,下载当前系统要用的 Mihomo,以及 service、sidecar 这些运行文件。

  4. 启动开发模式

    pnpm dev 是平时的开发命令。要直接跑 Tauri 的开发模式,用仓库里的 pnpm dev:tauri。

  5. 生成当前平台构建

    pnpm build 打出当前系统的安装包。仓库里的 pnpm build:fast 可以用来做更快的本地测试包。

shell
# 1. 克隆仓库
git clone https://github.com/clash-verge-rev/clash-verge-rev.git
cd clash-verge-rev

# 2. 启用包管理器并安装依赖
corepack enable
pnpm install

# 3. 下载 Mihomo 内核与运行时资源
pnpm run prebuild
# 需要重新下载时:
# pnpm run prebuild --force

# 4. 启动 Clash Verge 开发环境
pnpm dev
# 可选:pnpm dev:tauri

# 5. 构建当前平台
pnpm build
# 快速测试构建:pnpm build:fast

说明 这个仓库用 pnpm 管前端依赖。开发前先执行 pnpm run prebuild,把内核和服务文件准备好。命令以后可能改,所以同时核对 package.json 与 CONTRIBUTING.md。

代码大致分在哪几个目录

源码主要是三块:React 界面、Tauri 和 Rust 的桌面层、构建脚本。界面和状态主要在 src/。窗口、系统和 Rust 逻辑主要在 src-tauri/。

目录结构(示意)
clash-verge-rev/
├─ src/                    # React + TypeScript 前端
├─ src-tauri/              # Tauri 2 + Rust 桌面应用层
│  ├─ src/                 # Rust 业务与系统集成代码
│  ├─ resources/           # 应用运行资源
│  ├─ sidecar/             # Mihomo 等外部二进制资源
│  ├─ Cargo.toml           # Rust 依赖
│  └─ tauri.conf.json      # Tauri 构建、应用标识与打包配置
├─ scripts/                # prebuild、版本与发布辅助脚本
├─ .github/                # CI / GitHub Actions
├─ package.json            # 前端依赖与 pnpm scripts
├─ CONTRIBUTING.md         # 官方贡献与开发环境说明
└─ UPDATELOG.md            # 版本更新记录

说明 目录会随分支变,上面只帮你建立层级。改之前直接看当前仓库。改界面文案时,把各语言文件一起改,并跑项目要求的格式和检查。

界面怎么调用 Rust

界面通过 Tauri 调用 Rust。读系统状态、改本地配置或用桌面能力时,一般用 invoke 去调已经注册的命令。持续变化的状态,用事件监听来收。

typescript
import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";

// 调用 Rust 侧已经注册的 Tauri 命令
const result = await invoke("command_name", {
  payload: { key: "value" },
});

// 监听 Rust 侧事件
const unlisten = await listen("event-name", (event) => {
  console.log(event.payload);
});

// 页面或组件卸载时取消监听
unlisten();
rust
// 示意:Rust 侧通过 #[tauri::command] 暴露命令
#[tauri::command]
fn command_name(payload: serde_json::Value) -> Result<serde_json::Value, String> {
    Ok(payload)
}

fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![command_name])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

注意 上面的代码只说明 Tauri 的 invoke 和事件的通信方式,不是当前项目里的真实命令名。做具体功能时,从界面上的调用追到 src-tauri 里真正注册的命令和事件。

内核的控制接口

Mihomo 可以通过 external-controller 提供控制接口。设置了 secret 之后,请求要带上对应的鉴权信息。配置、节点、代理组、规则、连接、日志和实时流量,都可以由这些接口读取或修改。

shell
# 示例地址:请替换为 Clash Verge 当前实际的 external-controller
BASE=http://127.0.0.1:9090
AUTH="Authorization: Bearer 你的-secret"

# 读取 Mihomo 版本与当前配置
curl -H "$AUTH" "$BASE/version"
curl -H "$AUTH" "$BASE/configs"

# 修改运行模式:rule / global / direct
curl -X PATCH -H "$AUTH" -d '{"mode":"rule"}' "$BASE/configs"

# 查看代理与策略组
curl -H "$AUTH" "$BASE/proxies"

# 查看活动连接
curl -H "$AUTH" "$BASE/connections"

# 实时流量与日志可使用 GET / WebSocket
# ws://127.0.0.1:9090/traffic?token=你的-secret
# ws://127.0.0.1:9090/logs?level=info&token=你的-secret
接口 方法 用途
/version GET 看内核版本,也用来确认接口是否通
/configs GET / PATCH / PUT 读取配置、局部修改模式或端口,或整份重载
/proxies GET 列出全部节点、策略组,以及当前选中的项
/proxies/{name} GET / PUT 看某一个节点,或给策略组换节点
/proxies/{name}/delay GET 指定测试地址和超时,返回延迟
/rules GET 当前生效的规则,用来确认有没有按预期加载
/connections GET / DELETE 正在进行的连接,也可以断开其中一条
/traffic、/logs WebSocket 实时的上传、下载和日志

注意 例子里的端口只是演示。Clash Verge 实际端口以当前配置为准。 external-controller 这个接口权限很高。调试时尽量只监听本机,并设置一串可靠的 secret。

配置和日志在本机的哪里

设置、订阅、生成出来的配置、脚本和日志都在本机。查配置加载、生成、迁移或启动问题时,先找到当前的应用数据目录和日志。

系统 大致位置 里面有什么
Windows %APPDATA%\<应用标识>\ 客户端设置、订阅、生成的配置、扩展和脚本,以及 Mihomo 和应用日志
macOS ~/Library/Application Support/<应用标识>/
Linux ~/.local/share/<应用标识>/

说明 应用标识写在 src-tauri/tauri.conf.json 里,但真实路径仍可能因系统和构建渠道不同。调试时优先用客户端里的「应用目录 / 日志目录」入口,改文件之前先备份。

提交代码前要做的检查

先读当前的 CONTRIBUTING.md。提 PR 之前,前端和 Rust 两边的检查、格式化和本地验证都要跑过。

  1. 先说清楚要改什么

    改动大、或者会改变行为时,先在 Issue 或 Discussion 里对齐。修一个已经说清楚的问题,也要带上原来的讨论。

  2. 从自己的分支改

    一个分支只做一件事,名字能看出意图,例如 fix/tray-menu-crash。

  3. 跑检查,并在本地验证

    前端跑 pnpm lint,Rust 侧跑 cargo clippy-all,再用 cargo fmt / pnpm format 把格式对齐。

  4. 提交需要签名

    贡献说明要求提交经过签名。说明写清楚,一次提交尽量只做一件事。

  5. 在 PR 里写你怎么验证的

    写上系统环境、改了什么、怎么验证的。改了界面就附截图。改了行为就写复现步骤和测试方法。

注意 项目是 GPL-3.0。提交代码,就是按同一协议授权。新增依赖时,在 PR 里说明为什么需要,以及协议是否兼容。

正式发布、自动构建和自己编译

安装包由项目的 CI 生成,发到 Releases。AutoBuild 是客户端的构建渠道。Mihomo 的稳定版或 Alpha 是运行时选的内核。两者不是一层。

渠道 用途 说明
正式发布 相对稳定,并留下版本记录 按版本标签发布文件。改了什么,看 Releases 和 UPDATELOG.md
自动构建和开发构建 用来验证较新的提交 给开发验证用,更新更频繁。不要把它和 Mihomo 的 Alpha 内核当成一回事
自行构建 自己在本地打的包 自己打出来的包,签名和发布流程可能和正式版不同,要标明来源

说明 GitHub Releases 是公开发布的地方。自己编译的包要写清来源和构建环境。在客户端里切换 Mihomo 稳定版或 Alpha,不是在换 Clash Verge 的发布渠道。

本地构建或启动失败

多半出在依赖、系统工具链、prebuild 下载的文件、WebView 或系统运行库。先看报错属于哪一层,再去对 Issues 和贡献文档。

现象 常见原因 怎么处理
装依赖时锁文件报错 用了 npm 或 yarn 改回 pnpm。删掉 node_modules 之后重新执行 pnpm i
开发模式一启动就退出 还没准备 Mihomo 或 service 这些运行文件 先执行 pnpm run prebuild。要重新下载就加上 --force
Rust 编译说找不到链接器 缺少当前系统的构建工具,例如 MSVC 或 Command Line Tools 补上构建工具,重新打开终端再编译
Linux 提示缺某个库 WebKitGTK 一类的开发包没装 按报错里的库名安装对应 -dev 包
下载内核超时 当前网络访问不到发布地址 给终端配好代理再试,或手动把文件放到对应位置后重跑
打包成功,打开却是白屏 前端资源没打进去,或 Windows 上缺少 WebView2 先确认前端构建结果在。Windows 上再补 WebView2
改了文案,一切语言又变回去 只改了一种语言 同步更新 src/locales/ 下所有语言文件

准备好再开始改

先按 CONTRIBUTING.md 把环境和 prebuild 准备好,从一个范围清楚的改动开始。提交前完成前端检查、Rust 检查、格式化,以及项目要求的签名验证。

Clash Verge 开发说明:本地构建与内核接口