Electron 更新方案浅析

149 阅读6分钟

Electron 更新方案浅析

在 Electron 应用的迭代过程中,版本更新是不可或缺的一环。如何在保证用户体验的前提下,实现稳定、高效、安全的更新机制,是每个 Electron 应用开发团队都需要解决的问题。本文将分享我们在实现 Electron 全量更新与热更新 过程中的技术方案、关键细节及经验总结。

一、设计目标

本次更新机制设计的目标主要包括:

  1. 实现热更新,且支持覆盖非 asar 部分的文件
  1. 支持后台下载,确保更新过程对用户无感或影响最小
  1. 全量更新等其他基础需求

通过以上目标,可以让用户在使用应用时获得更流畅的体验,避免频繁重启或等待更新安装的情况。

二、更新方案简述

2.1 全量更新

基于 electron-updater 实现,支持完整的应用版本更新,本质是下载安装包后进行安装操作。

2.2 热更新

基于文件替换机制实现:

  1. 签名处理:应用签名本身是对安装包的完整性验证,若直接对应用进行文件替换,本质上影响了签名验证,可能会导致应用打开失败(如 macOS),Windows 验证相对宽松,但本质上文件替换会导致签名失效。
  1. 热更新场景及方案
    • 依次更新:适用于部分游戏应用场景,下载多个热更包
    • 差量更新:生成所有历史版本与最新版本的差量包,根据当前版本选择对应的热更包进行更新
    • 降级处理:退化至全量更新,认知成本较低
  1. 技术选型:原设计基于 electron-asar-hot-updater,原理也是文件替换,但仅支持 asar 文件,且请求更新信息时仅支持 POST 请求,不利于 CDN 式响应(原方案在本地启动了 Node 服务进行转发)。

三、全量更新

3.1 版本检测

当应用启动或用户手动触发更新时,electron-updater 会执行:

autoUpdater.checkForUpdates() 其内部会访问配置文件中的更新 URL(例如来自 package.json 的 publish 字段):

"publish": [{
  "provider": "generic",
  "url": "https://update.example.com/releases/"
}]

服务端需返回一个 YAML 格式的最新版本信息文件(通常名为 latest.yml 或 latest-mac.yml),其内容示例:

version: 2.27.0
files:
  - url: peanut_test_CUCU_2.27.0_202510091527.exe
    sha512: 23i2/6XRpJG5DIEzlaz+LEQR2NhTYASri0ob1O/6VZURX3BGo0jZLMh4n33Be4u1ZV8+EcugIVuGlzkcLrHChw==
    size: 201224256
path: peanut_test_CUCU_2.27.0_202510091527.exe
sha512: 23i2/6XRpJG5DIEzlaz+LEQR2NhTYASri0ob1O/6VZURX3BGo0jZLMh4n33Be4u1ZV8+EcugIVuGlzkcLrHChw==
releaseDate: '2025-10-09T07:28:53.684Z'

electron-updater 会读取当前版本号 (app.getVersion()) 与该文件的 version 字段对比:

  • 若版本号不同(且服务端版本更高),则触发下载
  • 若版本号相同但配置中启用强制更新,则也可下载
  • 若允许 downgrade,且版本号低于当前版本号,则也可以下载

3.2 更新包下载

autoUpdater.downloadUpdate() 触发下载,UI 层监听 download-progress 事件实现进度更新。

文件缓存在 appdata/local 文件夹下,支持文件完整性校验及断点续传。

3.3 安装与重启

下载完成后,autoUpdater.quitAndInstall() 触发安装。

注意autoInstallOnAppQuit 此配置默认为 true,若存在已下载未安装的更新包,会在退出应用时自动更新。

四、热更新

4.1 版本检测

当存在热更新时,updater 模块会请求 CDN 中的更新配置,获取详细的更新信息,其内容示例:

{
  "versionCode": 22814,
  "minimumVersionCode": 22813,
  "file": {
    "name": "resources.zip",
    "url": "http://192.168.91.87:8080/resources.zip",
    "targetPath": "./resources",
    "md5": "487f7b22f68312d2c1bbc93b1aea445b",
    "size": 190971754
  },
  "fullFile": {
    "name": "resources.zip",
    "url": "http://192.168.91.87:8080/resources.zip",
    "targetPath": "./resources",
    "md5": "487f7b22f68312d2c1bbc93b1aea445b",
    "size": 190971754
  }
}
  • versionCode 为热更版本号
  • minimumVersionCode 为热更最低版本限制

上述版本号会跟当前版本进行对比:

  • versionCode 大于当前版本号,则存在有效更新
  • minimumVersionCode 大于当前版本号,则说明本地存在部分内容未更新的情况,强制回退至全量版本热更(建议是 resources 下所有文件)

4.2 热更包下载

下载更新 zip 包至缓存目录 hot-update,根据热更信息中文件 md5 值进行文件完整性校验。此处下载不进行解压缩操作,防止出现可能的文件篡改/丢失导致热更失败且不能回退的情况。

4.3 安装与重启

此方案基于文件替换,不同环境文件操作有所区别:

  1. 解压缩:将热更包解压缩
  1. 文件替换
    • 若是 macOS 环境,则直接 renameSync 或者复制后删除
    • 若是 Windows 环境,由于文件可能被应用占用,需要关闭应用后使用第三方程序进行移动
  1. 文件合并:主进程使用 spawn 启动 updater.exeupdater.exe 会在启动五秒后进行文件合并操作,将热更文件夹与目标文件夹进行覆盖式合并:
    • 若此前不存在,则新增
    • 若此前存在,则覆盖
    • 不支持对文件进行删除操作

4.4 注意事项

  1. asar 文件处理:asar 文件下载/解压时先命名为 .tmp,再重命名,否则会报错
  1. 文件占用机制:Windows 文件占用机制与 macOS 不同,需要特殊处理
  1. 残留进程:如 app.quit 被阻断,导致进程未正确关闭,可以使用 process.exit(0)。当前使用的是等待 5s 方案

4.5 热更新-差分更新

electron-updater本身也实现了热更新。原理是基于 blockmap 文件进行差量对比。相当于将整个 exe 文件划分成 N 等分,根据新旧版本的 blockmap 对比结果,找出需要更新的分片然后下载。全部分片下载完成后组装成新的完整的 exe 安装包,再进行应用安装。

区别于 文件替换,此方案由 electron-updater 支持,仅需 publish 端保存新旧版本 blockmap 文件,且支持差分下载(CDN 或者 OSS 配置支持)即可。

相对于全量下载,下载量大大减少,基本为全量的 15%-35%,适合 resources 文件夹,尤其是 asar 文件夹较大的场景。

五、业务使用流程

  1. 检测更新信息:业务检测更新信息
  1. 强制更新判断:存在强更或者最低版本,则直接触发强制更新
  1. 后台下载:存在非强更则触发后台下载
  1. 用户手动更新:当用户手动更新时,会处理后台下载的进度更新事件,要么提示更新完成待安装,要么提示更新中,正确显示更新进度
  1. 热更新处理:若触发热更,且低于最低热更版本则触发全量热更