Nidalee 架构解密:Rust × Tauri × Vue,打造轻快而强大的桌面助手

约 6 分钟阅读

为什么选择 Tauri × Rust × Vue

Nidalee 的愿景是「高性能、体积小、原生感、安全合规」。Tauri 2 提供跨平台外壳与系统集成,Rust 负责安全高效的本地服务,Vue 3 则让界面表达与迭代更敏捷。

总览架构

  • 后端(src-tauri/):
    • 以 LCU(League Client API)为核心的模块化服务:auth/champ_select/gameflow/matches/perks/ranked/summoner/ 等。
    • 每个域包含 commands.rs(暴露 IPC)、service.rs(业务逻辑)、mod.rs(聚合入口),形成清晰的「命令网关 → 领域服务」分层。
    • 统一 request.rsunified_polling.rs:降低重复、控制轮询策略与并发。
  • 前端(src/):
    • 组合式 composables/(按域):连接、对战流程、选人会话、OP.GG、游戏资源、统一日志等;小而专一、可独立测试。
    • 状态(stores/):核心/功能/UI 三层命名空间;配合 pinia-plugin-persistedstate 保留关键设置。
    • UI:shadcn-vue + Tailwind + 自定义主题(OKLCH);复用通用组件与 features/ 功能组件。

Rust 侧领域划分(精简示意):

text
src-tauri/src/lcu/
├── auth/{commands.rs, service.rs}
├── champ_select/{commands.rs, service.rs}
├── gameflow/{commands.rs, service.rs}
├── matches/{commands.rs, service.rs}
├── perks/{commands.rs, service.rs}
├── ranked/{commands.rs, service.rs}
├── summoner/{commands.rs, service.rs}
├── request.rs
└── unified_polling.rs

典型 IPC → 服务的调用链(示例):

rust
// commands.rs
#[tauri::command]
pub async fn get_summoner_summary(app_state: State<'_, AppState>) -> Result<SummonerDto, AppError> {
    summoner_service::summary(&app_state.client).await
}

// service.rs
pub async fn summary(client: &LcuClient) -> Result<SummonerDto, AppError> {
    let data = client.get("/lol-summoner/v1/current-summoner").await?;
    Ok(data)
}

数据流与边界

  • IPC 命令仅承载「数据输入/输出」与「限流/鉴权/错误映射」,领域逻辑留在服务层,UI 通过 @tanstack/vue-query 管理请求与缓存,useCachedQuery 封装了稳定 key 与缓存策略。
  • 连接层提供「客户端心跳 + 重连 + 状态广播」,业务只关心「可用与否」而非具体重试细节。

前端以组合式能力封装请求与缓存(示例):

ts
// useSummonerAndMatchUpdater.ts(节选)
import { useQuery } from '@tanstack/vue-query'

export function useSummoner() {
  return useQuery({
    queryKey: ['summoner'],
    queryFn: () => invoke<SummonerDto>('get_summoner_summary'),
    staleTime: 60_000,
  })
}

生命周期与并发模型:顺畅但可控

  • Rust 端:
    • 任务使用 tokio 驱动;长生命周期的轮询放在 tauri::async_runtime::spawn 里,使用 broadcast 通道将事件扇出。
    • 资源清理通过 Dropselect! + shutdown 信号控制;显式中止任务,避免孤儿任务。
  • 前端:
    • 组合式函数 onMounted 建立订阅,onUnmounted 取消订阅与定时器。
    • 多请求并发时以 Promise.allSettled 聚合,避免“栅栏效应”;失败个体写入活动日志而不中断整体流程。

错误边界与回退:故障可预期、体验可恢复

  • IPC 命令返回统一 AppError,前端展示“非阻塞型错误提示”(如 toast),并提供重试。
  • 关键路径(如“自动接受匹配”)提供「状态机」式回退:失败后回到可恢复状态,不致于悬挂。

工程化与性能

  • 构建拆包(vite.config.tsmanualChunks):
    • vendor_vuevendor_piniavendor_tanstackvendor_tailwindvendor,提高缓存命中与首包稳定性。
  • 插件体系:
    • 开发:vite-plugin-vue-devtoolsunplugin-auto-importunplugin-vue-components
    • 生产:vite-plugin-remove-consolerollup-plugin-visualizer(可选分析)。
  • 规范:
    • Lint:oxlint + eslint,格式化 prettier;类型边界通过 vue-tsc 与 Rust 编译器共同保证。
    • 前后端类型对齐:scripts/sync-types.mjs + src-tauri 单测输出类型,再同步到前端 types/

多主题与动画策略:高对比、轻干扰

  • UI 主题采用 OKLCH 变量方案(styles/theme-oklch.css),在深浅主题下保持可读性与对比度;
  • 动画使用 motion-v 与轻量化自定义动画工具类,限制在 200–300ms 内,优先“位移动画”,减少重排成本。

功能域拆分的收益

  • 变更范围可控:新功能落在单一域(如 game-helper/),修改对其它域影响最小。
  • 可观测性:统一的 useActivityLogger 记录关键步骤,可挂接 UI 活动面板与日志导出。
  • 可测试:域服务可通过命令行/单测注入假数据;前端 composables 易于 stub。

安全与合规

  • 与官方 LCU API 交互,不注入、不修改内存,不劫持网络,避免外挂风险。
  • 敏感信息不持久化,遵循用户协议;提供显式免责声明与非商用 License。

收官

Nidalee 的关键在于「清晰的领域边界 × 统一的数据契约 × 稳健的工程化」。桌面应用也可以拥有现代 Web 的开发效率,同时不妥协于性能与稳定。

进一步阅读:

相关文章