从开发调试到用户能下载安装:我把相册打成安装包的经历

上一篇写了:从找一张图到做成产品:我定下的第一版边界

里面有一条定得很清楚:正式形态是本地安装包。

落地交付时,我也试过把部分依赖留到安装后再联网拉取,好把首包做小。测下来问题很实际:网络波动、镜像失效、权限限制,任何一环失败,用户都会卡在「装上了却跑不起来」。最后还是定成:为了让不同机器上的运行环境保持一致,Node、Python、Redis、ffmpeg 以及相关运行时,全部打进安装包。

这条路我自己也反复想过合不合理。坦白说,Electron 应用自带运行时、音视频工具内嵌 ffmpeg,都很常见;但把业务后端、Redis、Python AI 一整条栈都塞进安装包,并不是最轻量的主流做法。很多产品会把首包做小,第一次打开再下载模型或运行时。那样下载更快,失败模式却更多,售后成本也更高。

笑笑相册这种管电脑里长期影像库的本地相册,我更在意第一次打开稳不稳,而不是安装包能不能再瘦几百兆。所以最终还是选了「全量内嵌」:用体积和构建复杂度,换环境确定性。它不是最省流量的方案,但是和「本地、一致、少折腾」的产品目标是对齐的。

因此安装包从一开始就不是「一个前端壳 + 首次联网补齐」,而是自带完整本地运行时。

先说结论:安装包要装的,远不止一个前端页面

相册桌面版看起来是一个 App,里面其实至少有这些东西一起工作:

  • Electron 壳:开窗与开屏,按序拉起并监控本机子进程;试用与买断激活、安装包完整性、媒体库目录也落在这一层
  • 前端页面:媒体库浏览、搜索筛选、时间线 / 地点 / 人物 / 相似图、导入与设置等用户界面
  • Node API 与后台 Worker:导入入库、本地数据库、搜索与相册业务;并把分析、云描述、逆地理、视频转码等任务丢进队列慢慢消化
  • Redis:支撑这些后台队列,也承担导入去重一类需要快速读写的状态
  • Python AI:本地做人脸检测与聚类、图像向量(以图搜图),以及相似图用的感知哈希;需要时再配合云端做画面描述
  • ffmpeg / ffprobe:处理 HEIC 等难解码图片、读取视频信息、生成缩略图;部分视频在播放前还会按需转码

开发态可以分别启动;安装包态必须由主进程按顺序拉起、等待就绪、退出时再干净收掉。

换句话说,用户拿到的不是「一个网页套壳」,而是「一整套被打进安装包的本地服务」。

第一步:先让 Electron 跑起来

大概在今年四月,我先把产品推到 Electron 桌面形态。

那时候目标很朴素:同一套 Vue 业务代码,既能在浏览器里开发,也能在桌面窗口里打开。开发态仍然连本地 Vite;打包后再换成加载本地 dist

很快又补了 Windows,并开始认真碰打包配置。这一步解决的是「像不像桌面应用」。

真正「像不像可交付产品」,要更后面。

第二步:发现只打前端远远不够

按常规划法,很多人会先做:前端 build → electron-builder 出 dmg/exe。

我也走过这一步。结果很清楚:

  • 安装包能打开窗口
  • 但接口没有人应答
  • 分析能力更谈不上

因为开发时依赖 Vite 把 /api 代理到本机后端;安装包里没有 Vite 了。前端必须直连本机 API,而 API、Redis、Python 也得有人拉起------开发时这些是我手动开的,交付时就得交给壳来管。

所以接下来要补的,就不只是把页面打进包,而是:

把整条本地运行时一并打进包,并由 Electron 主进程编排。

现在启动大致是:开窗 / 开屏 → 拉起 Redis → 拉起 Python AI → 拉起 API → 健康检查通过 → 再进相册。退出时再统一停掉子进程,尽量不留僵尸服务占端口。

第三步:平台差异会把问题放大

macOS 和 Windows 不是改个图标就完事。

两边的 Python 环境、和系统架构绑定的本地扩展(比如数据库、图片处理那几块),再加上路径怎么写、程序文件叫什么------这些问题都会在打包阶段冒出来,Windows 和 macOS 都得各自花时间调通,没有捷径。对外发布时,两边理论上都该做代码签名;差别在于,Windows 上没签往往还能装,macOS 则严格很多------不但要签名,还要公证,少了这一环几乎过不了用户那一关。双击时经常会看到「已损坏,无法打开」一类提示:看起来像安装包坏了,其实是系统安全策略在拦。

顺带一提,未签名、未公证的包并非绝对不能用;自己调试时,终端里执行 xattr -dr com.apple.quarantine 应用路径,清掉隔离属性后往往就能打开,但这操作只适合开发者。给普通用户下载安装的产品,不可能让每个人装之前先敲命令。

最终打包被拆成了几条现实路径:

  • 自用 / 本地调试:可更快迭代,甚至保留不签名版本
  • 对外发布:签名、公证、校验更严
  • CPU 架构也得分开准备。我只有 Apple 芯片的机器,Intel(x64)没有机器实测,所以 Mac 这边先发布 Apple 芯片版

第四步:第一次打开,才是真验收

有一个问题印象很深。

第一次安装打开时,内嵌服务还没就绪,界面已经往前冲,结果直接弹出报错。这个问题是我在一台比较旧的戴尔电脑上测试时发现的------我的 MacBook 开发机启动很快,这类时序问题常常测不出来。

后来调整了启动流程:放宽就绪等待上限,启动不顺会自动多试几次,避免还没就绪就弹错。服务一就绪就继续,不会故意拖慢正常启动,平时用也不受影响。开屏加载服务时还加了开屏弹窗,让用户始终知道应用正在启动,不必对着空白干等焦虑。

第五步:包会越打越大,于是开始剪裁

把 API、AI、模型相关资源都塞进去之后,体积和构建复杂度都会涨。

再往后就不是「怎么打出来」,而是「怎么打得更干净」:运行时用不到的东西,打包阶段尽量清掉。用户几乎看不见这项工作,但它直接决定安装包会不会越发越臃肿。

第六步:发布前再补校验与安全

剪裁解决的是体积,发布链路还要再补两道:

  • 完整性校验,降低被改包的风险
  • 安全策略前后端、Python 侧一起收紧

这些同样偏后台,但决定了安装包能不能长期作为正式产品分发。

还有一件产品上的小事:程序目录和照片目录要分开

安装包还有一个容易踩的产品坑:把海量图库默认塞进安装目录。

笑笑相册后来定的是:程序装在应用目录,媒体库根目录可以放到用户自己的大盘;不在安装向导里强制绑死。

这样升级应用时,不容易把照片数据一起搅乱。

看起来像设置项,其实是安装包产品化的一部分。

安装包交出去,才算过了交付这一关

功能可以在开发环境里先做出来。

产品要过的那一关,是别人机器上的第一次双击。

把这条路压缩一下,大概是:

先做成桌面壳 → 再把前端打进包 → 再把 API / Redis / AI 一并编排进包 → 再过 Windows / macOS 差异 → 再过首次打开的冷启动就绪 → 再过剪裁 → 再过完整性校验与安全收紧。

写在最后

独立开发桌面应用,最容易高估功能,低估交付。

相册这种带本地 AI 的应用,交付成本尤其高------因为你交付的不是页面,而是一整套本机运行时。

相关推荐
devlei1 天前
Electron原理初探
electron
江米小枣tonylua6 天前
升级到 Prisma 7:“Rust 除锈” 带来的意外红利
electron
还好还好不是吗10 天前
MatrixMedia v0.10.x 更新:B站封面自定义 + 二开指南,开源项目的开发者体验再升级
electron·开源
习明然10 天前
我的本地化AI项目(三)
人工智能·python·electron·c#·avalonia
Hyyy11 天前
为什么 macOS 应用一换 Bundle ID,之前授予的权限就全失效了?
macos·electron
月走乂山13 天前
Anything Analyzer MCP 401 Unauthorized 故障排查
electron·故障排查·mcp·claude code·anything analyzer
熊猫钓鱼>_>13 天前
Electron:当 Web 技术统治桌面
大数据·前端·javascript·人工智能·架构·electron·agent
程序员老刘13 天前
Opencode从Tauri切回Electron,桌面端技术选型别被体积陷阱带偏了
electron·ai编程
大意的百合14 天前
Electron 应用如何上架微软商店:从 MSIX 打包到商店提交
javascript·microsoft·electron