原文連接: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 的性能優化》。有興趣的同窗可點擊查閱哦。