Electron的自动更新能力完全由Squirrel提供。我们先来了解下Squirrel能够提供怎样的更新能力:
在 Windows 上
Squirrel提供了应用程序从打包到安装、更新各阶段的能力。换一句话说,如果需要使用自动更新能力,那么应用程序的打包、安装也需要Squirrel的参与。
通过Squirrel打包的应用程序,在安装阶段,无需用户选择安装目录,也没有UAC对话框,应用将自动安装到%USERPROFILE%\AppData\Local
目录下。
在更新阶段,无需用户做任何操作,Squirrel将在后台静默下载,待用户下次打开应用时就会替换为新的包。
出于后续自动更新的需要,应用的包文件,存放在以版本号命名的目录下。当有更新可用时,Squirrel下载新的包文件,采用同样的命名规则,存放在新目录下。应用的安装目录是这样的:
该目录下的Stetho.exe
相当于启动引导,当运行该可执行文件时,将选择最新版本的包中的文件执行。
也正因为此,这个%USERPROFILE%\AppData\Local\Stetho.exe
始终作为用户执行的入口,在第一次安装阶段,就建议以该可执行文件创建快捷方式(创建方法在下文有提到)。
在 MacOS 上
我们知道,在MacOS上,常规的应用程序(非AppStore)安装方式是打开.dmg
文件,将应用直接拖入应用目录中。应用图标也能够自动在启动台等处出现。Squirrel打包的应用程序,在安装阶段不会有差异。在更新时,Squirrel将静默下载新包,在应用程序关闭后将执行自动安装。你也可用在更新下载完成后,提示用户有更新,确认后便会重启应用顺便更新一下。具体表现,同网易云音乐Mac端(可以通过在官网下载体验)类似。
在Electron应用中集成Squirrel
根据Electron的文档,在 MacOS 上,无需做如何操作,已经内置Squirrel.Mac;在Windows中,就像前面说的,依赖于你的安装程序也要有Squirrel的参与,才能将Squirrel集成进来。而这个安装程序的生成,需要由你自己配置。像electron-winstaller, electron-forge 或者 grunt-electron-installer 均可。
笔者使用的是 electron-forge,就支持集成Squirrel到安装程序中,更是支持将一些配置透传到Squirrel,以实现一些安装程序的定制,例如安装程序可执行文件(非应用启动时使用的可执行文件)的图标、文件名,甚至是loading动画。
在Electron应用中处理更新逻辑
借助Electron主进程中提供的autoUpdater模块,我们不需要直接与Squirrel对接,抹平了Squirrel双端的差异,也不需要去看Squirrel复杂的文档。
在Electron主进程下,可以待app启动后,执行更新的检查(autoUpdater.checkForUpdates()
)。在此之前,必须先设置检查更新的服务器地址(autoUpdater.setFeedURL(options)
),对于服务器的搭建,下个章节将做介绍。
开发者可以自行设定条件(例如设定每1小时一次),重复进行检查更新。
当有可用更新,且Squirrel已完成更新的下载,将触发'update-downloaded'
事件,此时可用提醒用户更新可用,马上执行安装,亦或是不做任何提醒,当用户下次打开应用后,更新也会生效。
在autoUpdater的文档中,已经介绍得比较清楚了,此处不再详述。读者亦可参考Electron官方的示例文件。
搭建更新服务器
在示例文件中,可以看到feedURL
的设置方式:
const feedURL = `${host}/${repo}/${process.platform}-${process.arch}/${app.getVersion()}`
笔者推荐也是采用这种方式,将platform
, version
等通过path方式传递。原因很简单,Squirrel封装的更新请求方式如下:
GET ${feedURL}/?id=${appId}&localVersion=${version}&arch=${arch}
Query 中并没有 platform
。
对于直接托管在GitHub的项目,可以直接使用 https://update.electronjs.org/ 服务搭建更新服务器。对于需要进行私有部署的,就要参考这个服务的代码了。
直接访问update.electronjs.org示例应用的 API,可以发现API的格式。 https://update.electronjs.org/electron/electron-api-demos/win32/1.0.0
{
"name": "2.0.2",
"notes": "Updated to Electron 4.0.1",
"url": "https://github.com/electron/electron-api-demos/releases/download/v2.0.2/ElectronAPIDemosSetup.exe"
}
https://update.electronjs.org/electron/electron-api-demos/win32/1.0.0/RELEASES
612ABC1A6317213550284A508C98AB679690BA7C https://github.com/electron/electron-api-demos/releases/download/v2.0.2/electron-api-demos-2.0.2-full.nupkg 61704801
只需要按照这些格式实现 API 即可。
对于细节的处理,建议参考update.electronjs.org的实现,可能有诸如同platform
,不同arch
处理之类的逻辑。
MacOS端
需要实现 ${feedURL}
该API接口,响应JSON,包含以下几个属性:
pub_date: Date,
notes: string,
name: string,
url: string // 安装文件的地址
这些属性,将在 Electron 的 'update-downloaded' 事件中返回,供处理诸如弹窗的逻辑。也就是说,如果在你的应用中不需要,除了url,其他内容可以不返回。
Windows 端
需要实现的是 ${feedURL}/RELEASES
,该接口返回 RELEASES
文件。该文件可以在打包的输出目录中找到(和安装程序exe,nupkg文 件一起)
文件中引用了 .nupkg
文件的地址。该文件就是自动更新时下载的包文件。打包时该地址是相对路径,需要自行替换为完整的 CDN 地址。
const res = await ctx.curl(releaseLink, {
dataType: 'text',
});
text = res.data.replace(/[^ ]*\.nupkg/gim, nupkgLink);
全新下载
对于新用户,提供下载链接。此时 Windows 端提供安装程序exe
文件,而不是nupkg文件
。
在Squirrel.Windows安装阶段处理额外逻辑(创建快捷方式等)
在 Squirrel.Windows 的安装、卸载等不同阶段,将向你的应用程序传入不同的启动参数(事件列表)。
你可以这么处理他们:
var handleStartupEvent = function() {
if (process.platform !== 'win32') {
return false;
}
var squirrelCommand = process.argv[1];
switch (squirrelCommand) {
case '--squirrel-install':
case '--squirrel-updated':
// Optionally do things such as:
//
// - Install desktop and start menu shortcuts
// - Add your .exe to the PATH
// - Write to the registry for things like file associations and
// explorer context menus
// Always quit when done
app.quit();
return true;
case '--squirrel-uninstall':
// Undo anything you did in the --squirrel-install and
// --squirrel-updated handlers
// Always quit when done
app.quit();
return true;
case '--squirrel-obsolete':
// This is called on the outgoing version of your app before
// we update to the new version - it's the opposite of
// --squirrel-updated
app.quit();
return true;
}
};
像 electron-squirrel-startup 这样的包,实现了在这些不同阶段,对应用的快捷方式进行了管理。
当然,你也可以自己处理这些事件,可以实现诸如注册默认应用程序、注册 web Message等的管理,此处不再做介绍。