WWW.YOUINFO.SITE
标签聚合 战绩

/tag/战绩

LinuxDo 最新话题 · 2026-06-07 11:19:24+08:00 · tech

本帖使用社区开源推广,符合推广要求。我申明并遵循社区要求的以下内容: 我的帖子已经打上 开源推广 标签: 是 我的开源项目完整开源,无未开源部分: 是 我的开源项目已链接认可 LINUX DO 社区: 是 我帖子内的项目介绍,AI生成、润色内容部分已截图发出: 是 以上选择我承诺是永久有效的,接受社区和佬友监督: 是 项目开源地址: GitHub - wnzzer/rank-analysis: 基于Tauri 2 + Rust,构建的一个LOL 英雄联盟腾讯服战绩查询助手,创新式标签标记机制,一键分析的混子、牛马队友 · GitHub 大家好,我是只会写 bug 的靓仔。 之前发过几篇 LOL 排位分析工具的帖子( 项目介绍 | v1.2 更新 ),当时还是 Electron + Go 的架构。有老哥评论说"这么大?",确实如此,尤其我经常在网吧这种裸连 github 的环境中使用 —— 一个查战绩的小工具,安装包 128 MB ,下它比打一把排位还久。 因此去年花了几个月,把整个技术栈从 Electron+Go 迁到了 Tauri 2 + Rust 。结果: 安装包: 128 MB → 5 MB (缩小 96%,GitHub Release 截图为证) 冷启动: ~1.5s → ~500ms 进程架构: Electron 壳 + Go server 两个程序 → 单个 Tauri 二进制 (砍掉独立后端进程和 localhost HTTP 那一跳) 内存: ~306 MB → ~241 MB (WebView2 那部分是系统共享的,虽然 tauri 应用增加这个效果会变得更好) 下面把迁移过程和踩的坑记录一下,如果你也在做类似的桌面端项目,或者 Electron / Tauri 之间纠结,这篇应该能帮你省点时间。 先说老架构为什么非换不可 最早的设计是这样的: 但三个绕不开的痛点: 128 MB 劝退用户 Electron 自带 Chromium runtime,光这就 60~80 MB,加上 Go binary 30 MB+。v1.0 打出来 128 MB 。后来挤了又挤,稳定在 85~93 MB。 一个查战绩的工具下 100 多 MB,用户等下载的时间够再开一把了。这是后来下定决心换 Tauri 最直接的原因。 体积不是"技术债",是用户会不会用你的第一道门槛。 两个进程,启动和调试都难受 启动流程:Electron 起 → 拉起 Go server → Go 监听端口 → Electron 前端轮询 Go 是否就绪 → 才能调 LCU。任何一环卡住都是"加载中"。 Go 那边 panic 了,Electron 这边只看到 fetch 超时。调试得在两个终端间来回切,日志打两份。这种痛谁用谁知道。 localhost HTTP 是隐性税 每次前端调后端:JS 对象 → JSON 序列化 → HTTP body → loopback 网络 → Go 反序列化 → 业务逻辑 → 再原路返回。虽然是 loopback,HTTP 头解析、TCP 握手、JSON 双向序列化的开销都是实实在在的。查战绩这种"一次拉 10 个召唤师 + 各自 20 场"的场景,延迟肉眼可见。 为什么是 Tauri 2 + Rust Tauri 1 vs 2 :Tauri 1 在 Windows 用 Edge HTML 兜底,2 全面切 WebView2,兼容性和稳定性好太多。插件系统也重做了(v2),autostart / single-instance / fs 都有一等公民支持。还有新的 Capability + Permission 模型,后面会说到踩坑。 为什么不是 Go 了 :Tauri 所有 #[tauri::command] 、State、AppHandle、事件总线都是 Rust API。继续用 Go 等于再绕一层 cgo/HTTP,那迁了个寂寞。Rust 直接编译进 Tauri 主进程,单二进制分发,不需要子进程。 那为什么不是 Wails? 这个问题我猜 Go 老哥们第一个就要问 —— Wails 不就是 Go + 系统 WebView 嘛,照理说我后端一行都不用重写,最省事。我也认真纠结过。最后没选它,原因挺主观的:一是当时 Wails 在多窗口、自动更新、权限这些一等公民支持上,体感不如 Tauri 2 厚实,遇到坑社区里能搜到的答案也少;二是说实话有点私心,就想趁这个项目顺手学一下 Rust。所以这不是"Wails 不行",而是"我想学 Rust + 赌 Tauri 生态"。如果你就是想保留 Go 又要小体积,Wails 完全值得先试一把,别被我带跑。 UI 库也顺便从 TDesign 换成了 Naive UI ,暗色主题和表格组件更顺。这一步单独做了一周,没掺在迁移里。 迁移路径:不是一锅煮的 很多博客写"我用一个周末把 X 重写成 Y"。我没那么神。实际上是 两条线分头推 ,节奏完全不一样: 迁移线 起点 终点 耗时 前端壳 :Electron → Tauri 2025-03-30(v1.5.4 双轨试水) 2025-04-19(v1.5.6 纯 Tauri) ~1 个月 后端语言 :Go → Rust 2025-03-31 2025-12-13(删 4274 行 Go) ~8 个月 前端壳为什么 1 个月就切完了 用户用脚投票。 v1.5.4 同时发了 Electron 86 MB 和 Tauri 10 MB 两个包: 同一天、同一版本、同一功能集, 86.3 MB vs 10.2 MB 。这还纠结啥?2 周后 v1.5.6 就只发 Tauri 了。 后端为什么磨了 8 个月 因为对用户来说,Go server 也好、Rust 也好, 功能没差 。没有压差就不急: 2025-03-31 :新建 Tauri 目录,跟旧 Go 目录并存 2025-04 ~ 2025-12 :一个 endpoint 一个 endpoint 往 Rust 搬。期间 没冻需求 ,新功能照加 2025-12-13 : -4274 行 Go / 34 个文件 ,旧服务彻底删 为什么不一刀切?因为 用户在线 啊(虽然没多少用户)。项目每 1-2 周发一版,停下来做半年大重写不现实。旧 Go server 保留着继续吃 bug fix,新功能直接写 Rust(有点像最近 bun 迁移 rust 的思路),旧 endpoint 按频率从高到低搬过去,搬完一个前端就切流量。并存期间项目目录长这样(确实丑,但能持续发版): 三个大坑 每个都卡了我至少半天,写出来希望大家别再踩。 坑 1:LCU 自签证书 —— Rust 的 API 名把 “danger” 写脸上了 LCU API 是 HTTPS,但用的是每次客户端启动时动态生成的自签证书。Go 里关掉 TLS 校验很简单: tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } client := &http.Client{Transport: tr} 到了 Rust 的 reqwest : let client = reqwest::Client::builder() .danger_accept_invalid_certs(true) .build()?; 看到 danger_accept_invalid_certs 这个名字的时候我愣了一下 —— 但 LCU 场景下这是唯一解,Riot 不可能给你 CA 证书。 更大的坑是 port 和 auth-token 怎么拿。 这俩是 LCU 每次启动随机生成的,写在自己进程的命令行参数里( --app-port=xxxxx --remoting-auth-token=yyy )。网上清一色用 wmic 去查,但那玩意要 管理员权限 。我之前专门写过一篇 无管理员权限的获取方法(全网首发 Go) ,原理是调 Windows 的 NtQueryInformationProcess API: use windows::Win32::System::Threading::*; let h = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid)?; let mut buf = vec![0u8; 4096]; let mut ret_len = 0u32; NtQueryInformationProcess( h, ProcessCommandLineInformation, buf.as_mut_ptr() as _, buf.len() as _, &mut ret_len )?; // 解析 UNICODE_STRING → 正则抠 --app-port / --remoting-auth-token PROCESS_QUERY_LIMITED_INFORMATION 是低权限级别,拿不到敏感数据。但 进程命令行不算敏感 —— Windows 设计里被忽视的一个口子,刚好让我们绕过了管理员要求。 那 NtQueryInformationProcess 吐出来的那坨 buffer,怎么变成 port 和 token?谜底就在这块 buffer 的结构上 —— 它开头是一个 UNICODE_STRING ( Length + MaximumLength + 一个指向宽字符串的指针),真正的命令行数据就跟在后面。把宽字符串转成 String ,剩下的就是正则的活了: #[repr(C)] struct UnicodeString { length: u16, maximum_length: u16, buffer: *mut u16 } // buf 就是上面 NtQueryInformationProcess 填好的缓冲区 let us = unsafe { &*(buf.as_ptr() as *const UnicodeString) }; let wide = unsafe { std::slice::from_raw_parts(us.buffer, (us.length / 2) as usize) }; let cmdline = String::from_utf16_lossy(wide); // 命令行里把这两个随机值抠出来 let re_port = Regex::new(r"--app-port=(\d+)").unwrap(); let re_token = Regex::new(r"--remoting-auth-token=([\w-]+)").unwrap(); let port = re_port.captures(&cmdline).and_then(|c| c.get(1)).map(|m| m.as_str().to_string()); let token = re_token.captures(&cmdline).and_then(|c| c.get(1)).map(|m| m.as_str().to_string()); (边界检查和错误处理我省了,能看懂思路就行;完整实现在上面那篇全网首发的文里。) 坑 2:Tauri 2 的 Permission 模型 —— 配对了跑不通,报错还不告诉你 Electron 安全模型基本就是开/关 nodeIntegration 二选一。Tauri 2 是 白名单粒度 ,每个能力都要显式声明: { "permissions": [ "core:default", "shell:allow-open", "http:default", "core:window:allow-start-dragging" ] } 第一次写很容易陷入**“代码明明对了为啥跑不通”**的死循环 —— 因为权限不足的报错不指向配置文件,而是给你一个看起来像"函数不存在"的错误。比如我漏配 dialog 权限时,前端 invoke 收到的是这么一句: command plugin:dialog|open not allowed. Permissions associated with this command: dialog:allow-open 乍一看像命令名写错了,对着代码翻来覆去找不出毛病。其实谜底就在报错的后半句 —— 它已经把你要加的权限名( dialog:allow-open )报出来了,只是第一次根本不会想到往那看。我在这上面浪费了大半天。 后来踩熟了,记了几个具体坑: http:default 要写两遍 :第一遍启用 fetch 命令,第二遍配 URL 白名单 scope。只写第一遍,命令存在但 fetch 调用静默失败,连个报错都没有 动态窗口要通配符 :项目给每场对局弹详情窗口,label 是动态的( match-detail-{id} ),capability 里得写 "windows": ["main", "match-detail-*"] 。漏了就所有 command 调不通 core:window:allow-start-dragging :项目禁了原生标题栏,整个拖拽靠这个 API。漏配了窗口就拖不动,你猜我怎么发现的 经验 :每加一个新 Tauri command 或新窗口,第一件事查 capabilities/default.json 。权限先行,代码后行。 坑 3:Rust struct 和 TS type 的同步 —— 手动对齐的酸爽 旧前端调 Go 是 fetch ,新前端调 Rust 是 invoke : // 旧 const data = await fetch('/api/match-history?puuid=xxx').then(r => r.json()) // 新 const data = await invoke('get_match_history', { puuid: 'xxx' }) 表面上是换了个调用方式。但真正头疼的是: Rust 那边 struct 改一个字段,TS 怎么知道? 我试过 ts-rs、specta 这些自动生成工具,最后没用,纯手动对齐。原因很实际: 总共就十几个核心 struct,自动生成引入的构建管线复杂度超过了收益 Rust 这边统一用 #[serde(rename_all = "camelCase")] ,自动转 camelCase,写 TS 的时候照着来就行 TS 文件头注释直接标注对应的 Rust 文件路径: /** * AI 标签建议相关类型,与 Rust schema * (src-tauri/src/command/user_tag_config.rs) 严格同构。 */ CI 两边都跑 typecheck(前端 vue-tsc ,后端 cargo clippy ),能挡住大部分 但确实出过 bug —— Rust 的 RecentData 有 select_mode ,TS 那边多写了 wins 和 losses 两个字段,Rust 根本不返回这俩。运行时永远是 undefined , 不报错但数据是错的 。当时前端那块胜负数永远显示空,我对着前端代码看了半天没看出毛病,最后跑去 grep Rust 的 struct 才发现:人家压根没这俩字段。这种静默 bug 比崩了还可怕,你不专门盯根本发现不了。 建议 :struct 超过 30 个或者多人协作,老老实实用 ts-rs / specta 自动生成。手动对齐只在一个人写、量不大的情况下才省心。 顺手白嫖的一个好处:自动更新 这个本来没在计划里,迁完才发现是非常好用的功能。 Electron 时代我也想做自动更新,但 electron-updater 那套配下来挺烦:要么自己搭个更新服务器,要么拿 GitHub 当源,还要处理签名、增量包,配置一大坨。我当时嫌麻烦一直没正经做,全靠用户自己去 Release 页手动下新版。 Tauri 2 有个官方的 tauri-plugin-updater ,配置就几行: // tauri.conf.json { "plugins": { "updater": { "endpoints": ["https://github.com/wnzzer/rank-analysis/releases/latest/download/latest.json"], "pubkey": "你的公钥" } } } 更新源就是挂在 GitHub Release 上的一个 latest.json (等下"效果对比"那节,v1.8.2 截图里第一行那个 10.8 KB 的 latest.json 就是它),长这样: { "version": "1.8.2", "pub_date": "2026-05-24T00:00:00Z", "platforms": { "windows-x86_64": { "signature": "更新包的签名串", "url": "https://github.com/wnzzer/rank-analysis/releases/download/v1.8.2/lol-record-analysis-app-1.8.2-setup.exe" } } } 前端启动时调一下 check() ,有新版就提示下载安装,三五行的事: import { check } from '@tauri-apps/plugin-updater' const update = await check() if (update) { await update.downloadAndInstall() // 下完自动重启 } 这里有个容易搞混的点得说清楚 :Tauri updater 要你用一对自己生成的密钥( tauri signer generate ,免费)给「更新包」签名,公钥填进上面的配置 —— 这个签名是给自动更新做校验用的,和 Windows 上那种要花钱买的「代码签名证书」完全是两码事。我穷,没买代码签名证书,所以首次安装时 Windows SmartScreen 还是会弹个"未知发布者",这个坑我到现在没填。但自动更新本身一分钱没花就跑通了,对一个免费小工具来说,够用了。 效果对比(有图有真相) 安装包体积演变 每一行都是 GitHub Release 公开记录,可以去 Releases 页 核对: 版本 日期 大小 阶段 v1.0 2025-01-13 128 MB Electron + Go 起点 1.1 → 1.5.3 01~03月 ~85-93 MB Electron 时代 v1.5.4 2025-03-30 86.3 MB + 10.2 MB 双轨同框 v1.5.6 2025-04-19 10.3 MB 纯 Tauri v1.6.0 2025-10-08 6.7 MB 升 Tauri 2 v1.8.2 2026-05-24 5.01 MB 当前 起点 v1.0 —— 128 MB : 转折点 v1.5.4 —— Electron 和 Tauri 同框 : 当前 v1.8.2 —— 5.01 MB : 内存占用 旧版 Electron + Go(稳态 ~306 MB) : 新版 Tauri + Rust(稳态 ~241 MB) : WebView2 那 ~200MB 看着大,但 系统里只要装了 Edge 或者别的 Tauri 应用,这部分就是共享的 ,边际内存远小于这个数。 截图里 实用工具 (6) 的 6 个进程是 WebView2 的渲染 / GPU / 管理器等辅助进程 —— 和当年 Electron 的多进程模型一个道理,这部分跑不掉。前面"进程架构"那条说的"单个二进制",指的是 app 自己不再额外起一个 Go server 子进程 + localhost HTTP ,不是说 OS 层只剩 1 个进程。 几句废话 说实话,一开始决定迁的时候心里也没底。毕竟 Electron + Go 虽然,但它跑着呢。万一迁到一半翻车了,那才叫社死。 后来想通了: 128 MB 对一个查战绩的工具来说太离谱了。 用户不会关心你用的什么框架,他们只关心下完打开能不能用。Tauri 让安装包从 128 MB 变成 5 MB —— 不是"技术优化",是 让产品有被打开的机会 。 如果你也在做类似的桌面端项目,我的建议就三条: 别一刀切 ,留旧项目并存,新目录单独建,随时能 ship 先迁用户感知最强的功能 ,不是代码量最小的 别在迁移里掺重构 ,已经够复杂了,UI 改版分开做 就这些。希望对在做类似项目的朋友有帮助。 相关链接: 仓库: github.com/wnzzer/rank-analysis 最新版下载: releases/latest LCU 凭据获取(坑 1 详细实现): 无管理员权限 LCU auth-token、port 获取 历史版本: ① 项目介绍 | ② v1.2 | ③ 分页设计 觉得有用的话帮忙点个赞 有问题也可以去 GitHub 提 issue 交流 5 个帖子 - 5 位参与者 阅读完整话题

linux.do · 2026-04-18 12:22:30+08:00 · tech

今早战绩 提示词:创建超写实专业摄影​​,采用柔光效果。图里是北京市海淀区小学三年级下学期物理期末考试试卷,纸质版,使用中国常见的试卷形式,略微有褶皱,题目排版工整,其中题目是量子物理等新兴高等物理试题 提示词:创建超写实专业摄影​​,采用柔光效果。生成一张马斯克抖音直播带货的截图,星舰火箭只要19.9 经验就是疯狂试,时间长并且出二选一就是中灰度了 32 个帖子 - 23 位参与者 阅读完整话题