cc-switch 鸿蒙 PC移植:使用Tauri官方 feat/open-harmony 分支最新版本移植实录
2026-08-18 晚,基于 tauri 官方仓库 feat/open-harmony 分支(tauri 2.11.5,crates.io 官方最新版)重新移植 cc-switch 到 HarmonyOS PC。上一篇实录里用的社区 fork 停在 tauri 2.8.5,把整个依赖树锁死在两年前;这一次,插件全部用最新版,一个都不用降。
项目地址*:https://atomgit.com/qq8864/cc-switch-harmonyos
更多交流学习,欢迎加入开源鸿蒙PC社区:https://harmonypc.csdn.net/
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
猫哥的博客:https://blog.csdn.net/qq8864

起因:旧 fork 的版本债
第一次移植 cc-switch 到鸿蒙 PC 时,用的是社区维护的 fork(基于 tauri 2.8.5)。那个 fork 能跑,但代价很重:
tauri 2.8.5 → tauri-plugin-log 锁 =2.7.1、dialog 锁 =2.4.2、updater 锁 =2.9.0 …
上游插件早就进化到 2.9/2.10 了,我们却要为了兼容 2.8.x 的 API 面,把每个插件钉回老版本——这就是"版本债":能跑,但整个项目的基础设施停在两年前,以后想升级上游代码,先得还债。
于是我决定从官方 fork 一份 tauri,在 feat/open-harmony 分支上重新移植。结果发现:官方自己已经在维护 OpenHarmony 支持了。
| 仓库 | 分支 | 版本 |
|---|---|---|
| tauri-apps/tauri(经 yangyongzhen fork) | feat/open-harmony |
tauri 2.11.5(= crates.io 官方最新) |
| tauri-apps/wry | feat/open-harmony |
0.56.0 |
| tauri-apps/tao | feat/open-harmony |
0.36.0 |
| tauri-apps/cargo-mobile2 | feat/ohos |
0.22.4 |
wry、tao、cargo-mobile2 三个底层库的 OHOS 支持分支都在 tauri-apps 官方账号下——这不是民间 hack,是官方生态的一部分。
成果速览
| 验证项 | 结果 |
|---|---|
| Rust 交叉编译 | libcc_switch_lib.so(aarch64 ELF,strip 后 23.7MB,旧 fork 是 33MB) |
| HAP | entry-default-signed.hap(34.8MB,旧 fork 39.8MB,签名版) |
| 真机 | HUAWEI MateBook Pro HAD-W32,API 24,aarch64,hdc install + aa start 成功 |
| 进程 | 主进程 + GPU + render 三进程稳定存活 |
| 后端 | 数据库迁移、189 条模型定价、Copilot/Codex/Xai OAuth、全局代理全部初始化,0 错误 |
| 前端 | 从 tauri://localhost/assets/... 加载(生产模式),React 正常挂载,0 panic / 0 error_boundary |
| 插件 | log 2.9.0 / store 2.4.4 / process 2.3.1 / dialog 2.7.2 / fs 2.5.1 / opener 2.5.4 / updater 2.10.1 / deep-link 2.4.9 / window-state 2.4.1 / single-instance 2.4.3,全部 crates.io 最新 |
体积还变小了 10MB。新代码,新体积,何乐不为。
原理:还是那套,前端零改动
cc-switch (Rust + React)
→ libcc_switch_lib.so(napi-ohos 桥接,mobile_entry_point 宏展开)
→ OHOS ArkWeb WebView(渲染原前端,withGlobalTauri 全局桥)
→ HAP(hvigor 打包,签名后 hdc 部署)
React 前端原样打包,window.__TAURI__ 全局桥提供 IPC。Rust 后端交叉编译成 .so,由 RustAbility(@ohos-rs/ability)加载。原理没变,变的是依赖图和踩坑清单。
第一关:依赖解析,patch 的完全体
上次移植最大的坑是 links = "Tauri" 冲突(路径依赖不被解析器当作插件版本候选),解法是用 [patch.crates-io]。这次 fork 换新,同样的机制,但要 patch 的东西多了三样:
1. tauri 系列:path patch(和上次一样)
[patch.crates-io]
tauri = { path = "D:/tauri-ohos-new/crates/tauri" }
tauri-build = { path = "D:/tauri-ohos-new/crates/tauri-build" }
tauri-macros = { path = "D:/tauri-ohos-new/crates/tauri-macros" }
tauri-codegen = { path = "D:/tauri-ohos-new/crates/tauri-codegen" }
tauri-utils = { path = "D:/tauri-ohos-new/crates/tauri-utils" }
tauri-runtime = { path = "D:/tauri-ohos-new/crates/tauri-runtime" }
tauri-runtime-wry = { path = "D:/tauri-ohos-new/crates/tauri-runtime-wry" }
2. wry / tao:git patch,而且要钉版本
这是新旧 fork 最大的差异。旧 fork 直接把 wry/tao 写成 git 依赖(wry = { git = "https://github.com/richerfu/wry" }),自带 OHOS 实现;新 fork 回到官方风格——tauri-runtime-wry 里声明的是 registry 版本:
wry = { version = "0.56.0", ... }
tao = { version = "0.36.0", ... }
而 OHOS 支持在 tauri-apps 官方仓库的分支里。问题来了:fork 根 Cargo.toml 里的 [patch] 只对 fork 自己的 workspace 生效,不跨工程继承。所以 cc-switch 必须自己补 patch:
wry = { git = "https://github.com/tauri-apps/wry", branch = "feat/open-harmony" }
tao = { git = "https://github.com/tauri-apps/tao", branch = "feat/open-harmony" }
光 patch 还不够。registry 上 wry 已经发到 0.56.1,而分支是 0.56.0——需求 ^0.56.0 会解析到更高的 0.56.1,绕过 patch 回到 registry 版(没有 OHOS 支持)。必须显式钉死:
[dependencies]
wry = { version = "=0.56.0", default-features = false }
这种"patch 版本低于 registry 最新"的坑,以后官方分支一更新就没了,但当下必须钉。
3. openharmony-ability:本地 path patch,钉 0.3.0
最隐蔽的一个。wry 的 OHOS 段依赖:
openharmony-ability = { git = "https://github.com/harmony-contrib/openharmony-ability.git", features = ["webview"] }
注意 features = ["webview"]。而 openharmony-ability 这个仓库后来大重构了:main 分支已经变成 1.0.0-beta.1,把 webview 拆成了独立的 plugin-webview crate,主 crate 上根本没有 webview feature——于是:
failed to select a version for `openharmony-ability`
package `wry` depends on `openharmony-ability` with feature `webview`
but `openharmony-ability` does not have that feature
解法:钉回 fork 的 Cargo.lock 里用的那个 commit(295a276,版本 0.3.0,当时 webview 还在主 crate 里)。但 cargo 有个规则:patch 不能指向同一个 source(不允许 git + rev 覆盖同一个 git URL)。绕法是把 0.3.0 克隆到本地,用 path patch——source 不同,合法:
[patch."https://github.com/harmony-contrib/openharmony-ability.git"]
openharmony-ability = { path = "D:/openharmony-ability-030/crates/ability" }
openharmony-ability-derive = { path = "D:/openharmony-ability-030/crates/derive" }
(这是本机构建的硬依赖,换机器要重拉:git clone 后 git checkout 295a276。)
4. 插件与 npm 包:全部升到最新
依赖解析通过后,插件版本从"锁定旧版"改成"最新版":
tauri-plugin-log = "2.9.0"
tauri-plugin-store = "2.4.4"
tauri-plugin-dialog = "2.7.2" # 桌面段
tauri-plugin-updater = "2.10.1" # 桌面段
# …全部最新
然后 npm 侧必须同步——新版 CLI 会校验 Rust crate 与 @tauri-apps/* npm 包的 major/minor 一致,不匹配直接拒绝构建:
Error Found version mismatched Tauri packages.
tauri (v2.11.5) : @tauri-apps/api (v2.8.0)
把 package.json 里的 @tauri-apps/api 升到 2.11.1、@tauri-apps/cli 升到 2.11.4、各插件 npm 包同步升,再 pnpm install。
到这里,cargo update 一次通过——没有任何版本冲突。这就是新 fork 的价值:依赖树和官方最新生态对齐,解析器不再打架。
第二关:编译,13 分 26 秒
cargo tauri ohos build -t aarch64(记得 OHOS_HOME / OHOS_NDK_HOME 指向 SDK 根目录)。全量依赖树第一次编译,13m26s,一次通过——比旧 fork 顺畅多了。中间只踩了两个小坑:
坑 A:capabilities 的 platform 值。
我把 ohos.json 里的 platforms 写成了 "OpenHarmony",构建脚本报:
failed to parse JSON: unknown variant `OpenHarmony`,
expected one of `macOS`, `windows`, `linux`, `android`, `iOS`, `openHarmony`
新 fork 的 Target 枚举有 #[serde(rename_all = "camelCase")],OpenHarmony 序列化成 openHarmony——和旧 fork 一样,是我自己想多了,改回去就好。
坑 B:pnpm 的 store 写不进 E 盘。
ERR_PNPM_EPERM Failed to add tarball ... E:\.pnpm-store
E 盘根目录的 pnpm store 没权限,换个位置:pnpm install --store-dir D:/pnpm-store。这是开发机环境问题,和鸿蒙无关,但确实卡了我几分钟。
第三关:HAP 装配与签名
.so 编好后,Windows 上 HAP 装配还得手工来(CLI 的 ohpm/hvigor 在 Windows 上拉不起 .bat),流程和上次一样:
# rawfile 同步(CLI 不会自动做)
cp -r dist/. src-tauri/gen/ohos/entry/src/main/resources/rawfile/
# ohpm 装依赖
cd src-tauri/gen/ohos && ohpm install && cd entry && ohpm install
# 打包 + 签名
node "D:/Program Files/Huawei/DevEco Studio/tools/hvigor/bin/hvigorw.js" \
assembleHap --mode module -p product=default --no-daemon
签名:这次不用再登华为账号
cargo tauri ohos init 重新生成工程时会清空 signingConfigs。上次的解法是 DevEco Studio GUI 里重新登录华为账号自动生成,这次我发现材料都在 ~/.ohos/config/ 里躺着,而旧的 build-profile.json5 还在 git 历史里——直接把 material 段恢复回去就行:
"signingConfigs": [{
"name": "default",
"type": "HarmonyOS",
"material": {
"certpath": "C:\\Users\\yang\\.ohos\\config\\default_ohos_eeumV….cer",
"keyAlias": "debugKey",
// …p7b / p12 / 两个密码(加密后的 hex)
}
}]
命令行 hvigorw 直接复用,不需要 GUI。
hvigorfile.ts:还得净化
新模板依然带着 cargo 钩子(dev-eco-studio-script),Windows 上一跑就炸。替换成无钩子版:
import { hapTasks } from '@ohos/hvigor-ohos-plugin';
export default {
system: hapTasks,
plugins: [] // cargo 钩子已移除:.so 由 cargo tauri ohos build 手动编译
}
还有一个环境细节:hvigor 的 es2abc/Java 子进程要 cmd.exe 和 java——MSYS bash 的 PATH 转换会让 Node spawn(cmd.exe) 直接 ENOENT。构建前 export 纯 Windows 风格 PATH(含 C:\Windows\System32 和 DevEco 的 jbr\bin)。
第四关:前端,一个被 import 提升坑到的 polyfill
应用跑起来了,但控制台多了一行:
[i18n] Failed to read stored language preference
TypeError: Cannot read properties of null (reading 'getItem')
ArkWeb 在自定义 scheme(tauri://localhost)下 window.localStorage 是 null,这个上次就知道,也写了 polyfill。但这次 polyfill 没拦住 i18n 的报错——原因很微妙:ES module 的 import 是提升的,i18n 模块的顶层代码在 main.tsx 的任何运行时语句之前执行,而 polyfill 写在 main.tsx 的函数体里,晚了一步。
解法:把 polyfill 从 JS 模块挪到 index.html 的内联 <script>——它在 module script 之前执行,谁都比它晚:
<script>
// ArkWeb 自定义 scheme 下 window.localStorage 为 null。
// 必须在任何 ES module(i18n 等)执行前安装内存降级实现。
if (typeof window !== "undefined" && !window.localStorage) {
var map = new Map();
Object.defineProperty(window, "localStorage", {
value: {
getItem: k => map.has(k) ? map.get(k) : null,
setItem: (k, v) => map.set(String(k), String(v)),
removeItem: k => map.delete(k),
clear: () => map.clear(),
key: i => Array.from(map.keys())[i] ?? null,
get length() { return map.size; },
},
configurable: true, writable: true,
});
}
</script>
<script type="module" src="./main.tsx"></script>
重部署后,[i18n] Failed… 消失。注意:改前端必须走完整链路——vite 重建 dist → cargo tauri ohos build(新 dist 内嵌进 .so)→ 同步 rawfile → 重打包 HAP → 卸载重装(清 ArkWeb 缓存)。
收尾:真机验证
hdc install -r entry-default-signed.hap
hdc shell "power-shell wakeup" # 屏幕锁定时必做
hdc shell "aa start -a EntryAbility -b com.ccswitch.desktop"
验证三件套:
# 1. 进程:主 + GPU + render 三进程存活
ps -ef | grep ccswitch.desktop
# 2. 后端日志:189 定价、OAuth、代理,0 错误
cat /data/app/el2/100/base/com.ccswitch.desktop/files/.cc-switch/logs/cc-switch.log
# 3. 前端:生产模式资产加载,0 panic / 0 error_boundary
hilog -x | grep -iE 'ARKWEB-CONSOLE|panic|fatal'
全部通过。剩下几条 console 消息都是已知限制的优雅降级:更新检查(updater 在 OHOS 段不注册,ACL 拒绝)、窗口主题切换(返回"当前平台不支持")——都不是崩溃,前端都接住了。
经验总结
- 能上官方分支,就别用社区 fork。
feat/open-harmony在 tauri-apps 官方账号下,和 crates.io 最新版同步(2.11.5),插件零降级,依赖解析一次过。 - fork 的 workspace patch 不跨工程继承。用别人的 fork,它 patch 的 wry/tao/openharmony-ability 你得自己补;而且 patch 版本低于 registry 最新时,要显式钉
=x.y.z防绕过。 - git 依赖的"无 rev"是定时炸弹。openharmony-ability 的 main 一重构,所有无 rev 的 git 依赖全炸;钉 commit 才是可复现的构建。
- ES module import 提升会把"运行时 polyfill"变成"晚到一步"。平台 hack 类代码,能放
index.html内联就别放 JS 模块。 - 签名材料、环境变量、hvigor 钩子这些"一次性配置",全都要沉淀到文档和 git 历史里——重来一遍时它们就是救命稻草。
配套完整技术手册(17 个坑的解法、命令速查)见同目录 cc-switch-to-harmonyos-porting-guide.md(§0 为本次官方 fork 更新记录)。
更多推荐




所有评论(0)