原文连接:Auto-updating apps for Windows and OSX using Electron: The complete guidehtml
2017.11.06 更新:electron-builder 提供了 electron-updater 模块,具体请查阅:《Quick and painless automatic updates in Electron》。node
因为我以前也调研了 Electron 的自动更新方面的知识,因此我会在保留原文全部信息的前提下,加入了一些备注(如做者的一些错误信息和补充了我我的的一些认识)。git
经过 Electron,你可能只需一眨眼的时间就完成了一个不错的桌面应用,并分发到用户手中。当你以为本身能像一个侥幸的坏蛋同样轻松时,你可能会意识到你遗漏了一个重要的点:用户如何获取下一个版本呢?甚至该新版本新增了一些优秀的功能。固然,他们能删除后再从新安装该应用,但这难道不蹩脚吗?github
快速浏览 Electron 文档 时,你会注意到该文档中含有 auto-updater 模块,它仅仅是另外一个框架——Squirrel 的接口。Squirrel 会在背后检测(或你主动触发)是否有新版本、下载新版本,并在你启动或重启应用时自动更新应用。mongodb
但悲伤的是:实际实现起来并非文档上写的这么简单。由于自动更新在 OSX 和 Windows 上的工做方式并不相同(目前并不支持 Linux),而且这二者的文档是分散在多个库(repository)中。我已经花费了大量的时间把该功能实现了。因此我以为将我所学习到的知识总结成一篇教程是值得的,但愿它能节省你的时间。express
虽然这里所讲的一切应该均能在 Windows 和 OSX 上运行,但为了减小异议,我先声明我是在 Mac OSX 10.11 上执行的操做,除了为 Windows 系统构建安装包(在虚拟机上)。npm
如对该篇教程有任何改善或更新的建议,可在 twitter 联系我!编程
在实现自动更新以前,有一个重要的步骤 —— 打包。我假设大多数人已经知道如何经过 electron-packager 实现该操做,但有两件事是时常被忽略的。json
{ "name": "MyApp", "main": "app.js", "private": true, "productName": "MyApp", "version": "1.0.0", "author": "My Company Ltd", "description": "MyApp", "devDependencies": { "electron-installer-squirrel-windows": "^1.3.0", "electron-packager": "^5.1.1", "electron-prebuilt": "0.36.7" }, "scripts": { "start": "NODE_ENV=development ./node_modules/.bin/electron .", "pack:osx": "./node_modules/.bin/electron-packager . $npm_package_productName --app-version=$npm_package_version --version=0.36.7 --out=builds --ignore='^/builds$' --platform=darwin --arch=x64 --sign='Developer ID Application: My Company Ltd (ABCDEFGH10)' --icon=icon.icns --overwrite", "pack:win": "./node_modules/.bin/electron-packager . $npm_package_productName --app-version=$npm_package_version --version=0.36.7 --out=builds --ignore='^/builds$' --platform=win32 --arch=ia32 --version-string.CompanyName='My Company Ltd' --version-string.LegalCopyright='Copyright (C) 2016 My Company Ltd' --version-string.FileDescription=$npm_package_productName --version-string.OriginalFilename='MyApp.exe' --version-string.InternalName=$npm_package_productName --version-string.ProductName=$npm_package_productName --version-string.ProductVersion=$npm_package_version --asar=true --icon=logo.ico --overwrite" } }
package.jsonc#
注意 package.json 的额外字段 —— productName、author 和 description,虽然这几个字段并非打包必备的,但它们会在 Windows 的 Squirrel 安装包中使用到。
为应用执行代码签名(Code-signing)的这部操做并非自动更新的必备步骤(译者注:也许做者当时的 Electron 版本的自动更新模块没必要进行代码签名,但当前版本是必需要进行这部操做的,官方文档中写道:Your application must be signed for automatic updates on macOS. This is a requirement of Squirrel.Mac
. ),但这是很是可取的操做。对于 OSX,你须要一个 Apple 的开发者认证,而后在 script 字段的 pack:osx
替换如下参数便可:
--sign='Developer ID Application: My Company Ltd (ABCDEFGH10)'
在 OSX 中,你能够经过 Keychain Access > My Certificates 查看(应用程序 -> 钥匙串 > 个人证书,若是有的话)。
我并无在 Windows 上执行代码签名这项操做,但你能够看看该主题相关的优秀教程。
对于 Windows,推荐为 electron-packager 传递 version-string 的全部可选参数,如 company name、product name 等。由于一旦咱们生成 Windows 的 Squirrel 安装包,该应用就能在 Windows 的『开始』菜单显示正确的元信息(metadata),而不是 Atom 的默认信息。
Atom Shell is now called Electron。
因此,让咱们开始吧!
在 OSX 中,自动更新是经过 Squirrel.Mac 处理的,它是内置于 Electron 中。这意味着你只需打包你的应用,而后照常运行就好!
恩,其实不彻底是。
Squirrel.Mac 的工做方式是经过访问一个你所提供的 API 『路径』(endpoint),判断是否有新版本。若是没有新版本,那么该路径应该返回 HTTP 204。若是有新版本,则它会期待接收一个 HTTP 200、且是 JSON 格式 的响应,其中包含一个 能获取 .zip 文件的 url。
PS:『路径』又称"终点"(endpoint),表示API的具体网址。
{ "url": "http://mysite.com/path/to/zip/MyApp.zip" }
在获得该 url 后,Squirrel 会构造一个 application/zip 的请求去访问该 url,下载相应文件,而后触发最终事件(下载完成)让你知道更新包即将安装。对于你来讲,全部事情的处理都是自动化的。
若是你不十分肯定服务器程序应该长什么样,可看看下面的一个超级小型的 Node.js/Express 服务,假定它的目录结构以下:
└── releases ├── darwin │ ├── 1.0.0 │ ├── 1.0.2 │ └── 1.0.3 └── win32
{ "name": "squirrel-version-checker", "version": "0.0.0", "private": true, "scripts": { "start": "PORT=80 node app.js", "dev": "./node_modules/.bin/nodemon app.js" }, "dependencies": { "express": "^4.9.8", "morgan": "^1.3.2" }, "devDependencies": { "nodemon": "^1.8.1" } }
基于 Node 的更新服务 package.json
'use strict'; const fs = require('fs'); const express = require('express'); const path = require('path'); const app = express(); app.use(require('morgan')('dev')); app.use('/updates/releases', express.static(path.join(__dirname, 'releases'))); app.get('/updates/latest', (req, res) => { const latest = getLatestRelease(); const clientVersion = req.query.v; if (clientVersion === latest) { res.status(204).end(); } else { res.json({ url: `${getBaseUrl()}/releases/darwin/${latest}/MyApp.zip` }); } }); let getLatestRelease = () => { const dir = `${__dirname}/releases/darwin`; const versionsDesc = fs.readdirSync(dir).filter((file) => { const filePath = path.join(dir, file); return fs.statSync(filePath).isDirectory(); }).reverse(); return versionsDesc[0]; } let getBaseUrl = () => { if (process.env.NODE_ENV === 'development') { return 'http://localhost:3000'; } else { return 'http://download.mydomain.com' } } app.listen(process.env.PORT, () => { console.log(`Express server listening on port ${process.env.PORT}`); });
一个简单地、用于测试 Squirrel.Mac 自动更新的 Express 服务器
这将会从本地的文件系统进行分发文件,但这不是理想的处理方式。个人建议是:将这些文件放置在 Amazon S3。
Amazon S3:Amazon Simple Storage Service
而后你能够在开发环境下,经过 Electron 访问该路径:
http://localhost:3000/updates/latest?v=1.0.1
?v=1.0.1 是你当前应用的版本。
如今你已经拥有了服务器程序和路径了,那么在应用中处理更新操做就十分简单了。
在 Electron 的主进程文件中,引入 auto-updater 模块,而后获取当前系统和应用的版本:
const autoUpdater = require('auto-updater'); const appVersion = require('./package.json').version; const os = require('os').platform();
而后配置路径,该路径会因系统(Windows 和 Mac)不一样而有所差别(至于缘由,会在 Windows 章节看到):
var updateFeed = 'http://localhost:3000/updates/latest'; if (process.env.NODE_ENV !== 'development') { updateFeed = os === 'darwin' ? 'https://mysite.com/updates/latest' : 'http://download.mysite.com/releases/win32'; } autoUpdater.setFeedURL(updateFeed + '?v=' + appVersion);
告诉 Electron 到哪里检测新版本
autoUpdater 模块提供了一些事件,你可经过渲染进程触发它们(译者注:经过 IPC 通信模块),想获取更多信息,可查阅 auto-Updater 文档页面 。相关交互的实现决定取决于你如何处理这些事件(如发生错误等),并通知用户。但你最后一步应该作的是:
autoUpdater.quitAndInstall();
将上述语句放在主进程文件后,应用会以新本版的形式重启。赞!
如你想象的那样,在 Windows 上实现自动更新是经过 Squirrel.Windows。但它的处理方式与 OSX 彻底不一样。
与 Squirrel.Mac 不一样的点在于:Squirrel.Windows 并不须要一个用于检测新版本的 API 路径,它须要的是一个文件服务器,因此你能够简单地将文件拖拽到 Amazon S3 bucket 上。另外,该 Squirrel 更新器并不内置于 Electron,它是一个第三方依赖。这意味着你须要为你所打包的 Windows 应用生成一个安装器,这样它才会包含 Squirrel 更新器。
Amazon S3 bucket:S3 的数据存储结构很是简单,就是一个扁平化的两层结构:一层是存储桶(Bucket,又称存储段),另外一层是存储对象(Object,又称数据元)。具体信息可查看 《亚马逊S3服务介绍》
好消息是:Windows 的安装包和更新器的运行过程顺滑的。由于当你启动 Setup.exe 时,你会发现安装和启动该应用是迅速的。没有无聊的安装向导和一直按“下一步”、最后按“完成”的步骤,否则与大多数 Windows 安装器一模一样。固然,它也能生成 delta packages,这让你在执行更新时,没必要下载整个应用,这真的是一流啊。
译者注:我经过 electron-builder 生成的 Windows 安装包与咱们常见的软件安装界面不太同样,他没有安装向导和点击“下一步”,只有一个安装时的 gif 动画(默认的 gif 动画以下图),所以也就没有让用户选择安装路径等权利。也许做者习惯了 Mac 的安装方式(即下面第二幅图),因此会以为 Windows 的安装包比较繁琐。
Windows 安装时 默认显示的 gif 动画
Mac 常见的安装模式,将“左侧的应用图标”拖拽到“右侧的 Applications”便可
若是你想为 Windows 应用生成常见的、须要点击“下一步”的(即用户可自定义的)安装包,能够经过 NSIS 程序,具体可看这篇教程《[教學]只要10分鐘學會使用 NSIS 包裝您的桌面軟體–安裝程式打包。彻底免費。》。固然,前提仍是经过 electron-packager 打包程序。
NSIS(Nullsoft Scriptable Install System)是一个开源的 Windows 系统下安装程序制做程序。它提供了安装、卸载、系统设置、文件解压缩等功能。这如其名字所指出的那样,NSIS 是经过它的脚本语言来描述安装程序的行为和逻辑的。NSIS 的脚本语言和一般的编程语言有相似的结构和语法,但它是为安装程序这类应用所设计的。
坏消息是(至少对于 Mac 用户):我不能在 OSX 上正确地生成安装包,因此我建议你下载一个 Windows 虚拟机(如 VirtualBox、parallels),并安装 Node.js。
译者注:我经过 electron-builder,可在 MacOS 中直接(即不经过虚拟机)生成 Windows 安装包(即Setup.exe)。具体可 查看这里。
假设你已经配置好并设置了正确的更新源,那么在上述 OSX 章节的代码基础上,还须要处理一些 Squirrel.Windows 事件,这些事件与 OSX 上的不一样。你能够查看该 案例。然而,这里提供一个更简单的方式,仅需安装 electron-squirrel-startup npm 模块:
npm install electron-squirrel-startup --save-dev
而后在 Electron 的主进程文件顶部添加如下一行语句:
if (require('electron-squirrel-startup')) return;
Squirrel.Windows 事件应该被尽早处理,显然,这是要走的路。
最后,为了生成安装包,咱们会使用 Atom 的 grunt-electron-installer。为何它是一个 grunt 插件,而不是一个简单的命令行工具——我不知道,但它就是解决方法。
更新:Electron 团队开发了一个独立的安装器打包工具——electron-winstaller,它拥有与 grunt task 一样的 API
将 Electron-packager 生成的 win32 文件夹打包压缩(zip),而后将其复制到虚拟机上。在该文件夹外(译者注:在解压后),你须要配置 grunt task,该 task 会生成安装包,所以你应该首先安装全部依赖:
npm install -g grunt-cli npm install grunt grunt-electron-installer --save-dev
假设 Windows 编译后的包放置在一个称为 MyApp-win32-ia32 的文件夹下。下面展现 Gruntfile 的样子:
module.exports = function(grunt) { grunt.initConfig({ 'create-windows-installer': { ia32: { appDirectory: './MyApp-win32-ia32', outputDirectory: './dist', name: 'MyApp', description: 'MyApp', authors: 'My Company Ltd', exe: 'MyApp.exe' } } }); grunt.loadNpmTasks('grunt-electron-installer'); };
须要注意的是:若是你想为你的文件和安装包进行代码签名(code-sign)操做,你也须要为该 task 配置提供全部参数。
运行该 grunt task 后,会在 ./dist 目录下产生一堆文件:
grunt create-windows-installer
你预期看到的与下面相似:
└── dist ├── MyApp.1.0.0.nupkg ├── MyApp-1.0.0-full.nupkg ├── RELEASES ├── Setup.exe
在下一次发布时,该安装器也会自动生成一个 delta packages。
如今进行最简单的一步 —— 拖拽这些文件到 S3 bucket 进行上传。而后 url 指向该文件夹(包含 RELEASES 和 nupkg 文件)。当应用运行在 Windows 系统上时,它会将该 url 设置到 updateFeed 参数上(由于咱们在先前的 OSX 章节处已实现)。
注意:目前有一个与安装器的 node-rcedit 模块相关的问题,该模块会在你尝试去修改 .exe 文件的一些元信息和替换默认图标(icon)时抛出错误。你能够在 这里查看该 issue。所以,目前若是你想为安装器文件修改 icon 或为其赋予实际数据,你可能不得不手动地经过 ResHacker 进行修改。
但愿这篇文章能做为一个好的起点,能帮助和服务每个正在为 Electron 应用实现自动更新的朋友们。若是你发现任何我遗漏的点,或有任何改善的建议,欢迎在 twitter 告诉我!另外,请记住 Electron 是一个快速发展的框架,因此要确保你阅读的是你当前版本的文档。Electron 的 API 也是更新十分频繁的。
另外,凹凸实验室基于 Electron 和 Vue 开发了一个 Excel 数据清洗工具 XCEL,并根据此项目总结出了一篇博文《XCel 项目总结 - Electron 与 Vue 的性能优化》。有兴趣的同窗可点击查阅哦。