项目背景与选型
我花3周时间用ElectronReact帮客户落了地1个跨平台桌面工具项目背景与选型上个月接了个活儿客户是做物流ERP的他们内部的调度系统跑在网页上但现场司机和仓管员反馈说用浏览器操作太慢了经常断网或者手机浏览器适配烂导致数据同步延迟高达15分钟。客户明确需要一套桌面端工具要求能离线工作且必须同时支持Windows和macOS。说实话一开始我纠结了很久是用Tauri还是Electron。Tauri确实更轻量Rust写后端也很香但试了一圈发现团队里没人熟悉Rust而客户那边的IT支持全是老Windows程序员对Node.js环境更熟。最后我选了Electron虽然包体积大但生态稳踩坑概率低。我们就基于Electron 30版本前端用React 19打包用Vite整个工程结构保持简单不搞那些花里胡哨的微前端架构。核心痛点主进程与渲染进程的数据同步最大的坑不是功能而是状态管理。早期版本里我在渲染进程里用了Redux想做一个全局的离线队列没网时存本地有网时异步上传。结果上线后客户反馈说“偶尔丢单”一查日志发现当网络快速切换比如从WiFi切到4G时Redux的状态更新还没落盘进程就被杀了数据全丢。当时我觉得只要用localStorage就行结果发现错了。localStorage是同步的大量数据写入会卡死UI线程。后来我改用了SQLite通过better-sqlite3库直接操作数据库。关键决策来了我是把数据库操作放在主进程Main Process还是渲染进程Renderer Process我选了放在主进程。为什么因为better-sqlite3是原生模块在渲染进程里跑涉及Node集成问题尤其是在macOS上签名和公证流程极其痛苦。放在主进程通过IPCInter-Process Communication调用虽然多了一次跨进程通信的开销但胜在稳定。javascript// main.js 中注册SQLite操作const Database require(better-sqlite3);const db new Database(app-data/local.db);ipcMain.on(save-offline-data, (event, payload) {const stmt db.prepare(INSERT INTO queue (data, timestamp) VALUES (?, ?));stmt.run(JSON.stringify(payload), Date.now());event.sender.send(save-success);});// 渲染进程中调用const { ipcRenderer } require(electron);ipcRenderer.send(save-offline-data, record);这段代码看着简单但坑死了。第一次测试时因为没加事务锁两个Tab页同时写数据库直接报错“database is locked”。后来加了db.pragma(journal_mode WAL);并限制写入频率问题才解决。窗口管理与自动更新策略除了数据持久化窗口行为也是个细节。客户希望工具最小化后不消失而是缩到系统托盘并且右键菜单能直接访问常用功能。Electron的Tray API很好用但我在macOS上遇到了一个怪象托盘图标在某些高负载下会闪一下再显示。排查了半天发现是主进程里一个定时器没销毁导致事件循环阻塞。把那个无用的setInterval去掉后图标瞬间稳定了。关于自动更新我用了electron-updater模块。这里有个重要的实战决策是走S3存储还是GitHub Releases客户是内网部署居多公网出口带宽有限。最终我们选了自己架的Nginx服务配置了简单的HTTPS证书。javascript// auto-update.js 核心逻辑autoUpdater.on(update-available, (info) {// 弹窗提示但允许用户稍后下载dialog.showMessageBox({type: info,buttons: [下载并安装, 稍后],message:发现新版本 ${info.version},});});autoUpdater.autoDownload false; // 关键关闭自动下载避免内网流量抖动导致中断有意思的是关闭autoDownload后用户主动点击下载的成功率反而提高了40%。因为内网环境下静默后台下载经常因为带宽被抢占而失败改为显式交互后用户知道在下载不会中途关闭应用。总结与避坑整个项目历时3周从开发到交付覆盖了Windows 10/11和macOS 13。最深刻的体会是桌面应用开发70%的工作量在“非功能需求”上比如进程管理、文件权限、网络状态监听而不是界面代码。React负责UIElectron负责壳中间那层IPC通信的设计决定了应用的健壮性。别贪心不要在渲染进程里滥用Node API把原生操作都收拢到主进程这是血泪教训换来的经验。本文基于实际项目经验整理欢迎在评论区交流技术问题。