Electron autoUpdater實現Windows安裝包自動更新的方法
前言
Electron幫助我們突破瀏覽器的界限,通過Electron構建的桌面應用擁有各種瀏覽器應用夢寐以求的能力。
Electron提供的autoUpdater還可以幫助我們實現桌面應用的自動更新。
文件結構
首先,我們已經有了一個基于Electron做的應用,項目中有兩個package.json。這樣做的一個原因是將devDependencies和dependencies分開了,另外就是不需要在打包的時候再去指定哪些依賴不需要一起打到安裝包里面去了(通過ignore參數)。
目錄結構類似于這樣:
myapp -node_modules -package.json -app -js -css -index.html -main.js -package.json
外面的package.json內容類似于:
{ "name": "myapp", "main": "app/main.js", "scripts": { "start": "electron ." }, "devDependencies": { "electron-prebuilt": "^1.2.7" } }
里面的package.json的內容類似于:
{ "name": "myapp", "version": "1.0", "main": "main.js", "description": "my app", "scripts": { "start": "electron ." }, "dependencies": {} }
注意里面的package.json中的name,version,description是必填的,接下來打包會用到。
electron-squirrel-startup
為了使最后的安裝包能夠實現自動更新,我們需要對現有的應用做一些改動,使它可以處理一些啟動或者安裝時的事件。
我們可以在main.js里面加入一些處理的代碼或者方便起見,我們可以直接使用electron-squirrel-startup。
先安裝:
npm install electron-squirrel-startup --save
因為需要在main.js里面用到,我們需要將其安裝在app里面。
在main.js里面使用它,第一行加入如下代碼即可:
if (require('electron-squirrel-startup')) return;
有興趣的童鞋可以一起跟我去看看electron-squirrel-startup做了什么事情,急著打包的童鞋可以直接忽略這一段:
在myapp/app/node_modules/electron-squirrel-startup下面有一個index.js:
var path = require('path'); var spawn = require('child_process').spawn; var debug = require('debug')('electron-squirrel-startup'); var app = require('electron').app; var run = function(args, done) { var updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe'); debug('Spawning `%s` with args `%s`', updateExe, args); spawn(updateExe, args, { detached: true }).on('close', done); }; var check = function() { if (process.platform === 'win32') { var cmd = process.argv[1]; debug('processing squirrel command `%s`', cmd); var target = path.basename(process.execPath); if (cmd === '--squirrel-install' || cmd === '--squirrel-updated') { run(['--createShortcut=' + target + ''], app.quit); return true; } if (cmd === '--squirrel-uninstall') { run(['--removeShortcut=' + target + ''], app.quit); return true; } if (cmd === '--squirrel-obsolete') { app.quit(); return true; } } return false; }; module.exports = check();
它的代碼只有短短幾十行,做的事情也很簡單,注意返回值為true的那幾行,基本上來說就是安裝時,更新完成時,卸載時 main.js都會被調用,我們需要根據不同的情況做不同的事情,完成這些事情后不要啟動應用(會出錯),直接退出就好。
正常啟動前我們需要去檢測是否有新的安裝包,之后下載新包,重新安裝,重啟應用,為了做到這一點,我們需要在main.js里面加入如下代碼:
app.on('ready', () => { //安裝后第一次啟動不去檢測更新,go做的事情就是啟動我們的應用 if (process.argv[1] == '--squirrel-firstrun') { go(); return; } /* 設置自動更新的feedURL,本地測試可以設置為類似于http://127.0.0.1:8080/latest * 在latest文件夾下放著三個我們的安裝文件(Setup.exe,RELEASES,myapp-1.0-full.nupkg),下面會講到 * / autoUpdater.setFeedURL(feedURL); autoUpdater.on('update-downloaded', function() { // 下載完成,更新前端顯示 autoUpdater.quitAndInstall(); }); try { // 不是安裝應用的情況下啟動下回出錯,此時直接正常啟動應用 autoUpdater.checkForUpdates(); } catch (ex) { go(); return; } // createWindow是我們自己定義的方法,用來創(chuàng)建窗口,此處用來創(chuàng)建檢測更新的窗口 createWindow({ name: 'updateWindow', url: 'check-for-updates.html', title: "checkForUpdates", icon: icon, frame: false, width: 1306, height: 750 }); });
自動更新后臺搭建
var express = require('express'); var app = express(); app.use(express.static('releases')); var server = app.listen(8080, function() { var host = server.address().address var port = server.address().port console.log("應用實例,訪問地址為 http://%s:%s", host, port); });
文件結構如下:
autoupdate-backend -package.json -index.js -node_modules -releases -latest
此時latest文件夾里面還是空的,之后我們開始打包,將打包出來的三個文件放在此處即可。
electron-packager
在myapp下安裝:
npm install electron-packager --save-dev npm install electron-packager -g
兩種安裝方式對應兩種使用方式,第一種在腳本中使用,第二種的命令行使用。
腳本中使用,小姐姐在這里借助了gulp,所以需要安裝gulp:
npm install gulp --save-dev npm install gulp -g
新建GulpFile.js,定義一個task:
var gulp = require('gulp'); var platform = 'win32'; var arch = 'ia32'; var appPath = 'app'; var packageOutPath = 'production/package'; var iconPath = 'app/favicon.ico'; gulp.task('generate-package', () => { generatePackage(); }); function generatePackage(callback) { var packager = require('electron-packager') packager({ dir: appPath, platform: platform, arch: arch, out: packageOutPath, icon: iconPath, /*桌面快捷方式名稱以及開始菜單文件夾名稱*/ 'version-string': { CompanyName: 'MyCompany Inc.', ProductName: 'myapp' } }, function (err) { if (err) { console.log(err); } else { callback && callback(); } }); }
需要打包的時候,打開命令行:
gulp generate-package
這樣做的好處是調用方便,當然我們也可以直接通過命令行調用electron-packager,前提是我們全局安裝了它或者將其安裝目錄添加到了環(huán)境變量中:
更多參數一一加上即可。
貼上官方文檔鏈接:
github鏈接:https://github.com/electron-userland/electron-packager
下面兩個鏈接在上面的文檔里面都能找到,但是個人感覺比較常用,還是貼出來:
參數使用:https://github.com/electron-userland/electron-packager/blob/master/usage.txt
腳本使用:https://github.com/electron-userland/electron-packager/blob/master/docs/api.md
打包
myapp下安裝electron-winstaller:
npm install electron-winstaller --save-dev
還是在gulp里面添加一個task,連同package的代碼一起貼上:
var gulp = require('gulp'); var platform = 'win32'; var arch = 'ia32'; var appPath = 'app'; var outName = 'myapp-win32-' + arch; var packageOutPath = 'production/package'; var installerOutPath = 'production/installer'; var packagePath = `${packageOutPath}/${outName}`; var installerPath = `${installerOutPath}/${outName}`; var iconPath = 'app/favicon.ico'; var gifPath = 'loading.gif'; gulp.task('generate-package', () => { generatePackage(); }); gulp.task('generate-installer', () => { isDirExist(packagePath, (exist) => { if (exist) { generateInstaller(); } else { generatePackage(() => { generateInstaller(); }); } }); }); function isDirExist(path, callback) { fs.readdir(path, (err) => { callback && callback(!err); }); } function generatePackage(callback) { var packager = require('electron-packager') packager({ dir: appPath, platform: platform, arch: arch, out: packageOutPath, icon: iconPath, /*桌面快捷方式名稱以及開始菜單文件夾名稱*/ 'version-string': { CompanyName: 'MyCompany Inc.', ProductName: 'myapp' } }, function (err) { if (err) { console.log(err); } else { callback && callback(); } }); } function generateInstaller() { var electronInstaller = require('electron-winstaller'); electronInstaller.createWindowsInstaller({ appDirectory: packagePath, outputDirectory: installerPath, loadingGif: gifPath, authors: 'ganyouyin', exe: 'myapp.exe', title: 'My APP', iconUrl: `${__dirname}/${iconPath}`, setupIcon: iconPath, setupExe: 'Setup.exe', noMsi: true }).then(() => console.log("It worked!"), (e) => console.log(`No dice: ${e.message}`)); }
之后執(zhí)行任務:
gulp generate-installer
第一次會非常慢,但是執(zhí)行完成后我們的安裝包就出來了。
此時我們的文件結構是:
myapp -GulpFile.js -package.json -node_modules -app -production -package -myapp-win32-ia32 - 各種文件,包含一個myapp.exe,雙擊可以直接運行 -installer -myapp-win32-ia32 -Setup.exe -RELEASES -myapp-1.0-full.nupkg
有了三個文件,將他們粘到之前的autoupdate-backend/releases/latest文件夾下面。
測試
- 啟動我們的自動更新后臺;
- 將myapp/app下的package.json里面的version改為1.1,再次打包;
- 將之前的autoupdate-backend中的latest文件夾重命名為1.0;
- 新建文件夾latest,將新打包產生的三個文件粘進去;
- 雙擊1.0里面的Setup.exe安裝應用;
- 關閉應用,雙擊桌面上的快捷方式myapp.exe再次打開應用;
不出意外此時就會去進行自動更新的操作,結束后自動重啟,再次打開時已經是1.1的應用。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
IE11下CKEditor在Bootstrap Modal中下拉問題的解決
這篇文章主要介紹了IE11下CKEditor在Bootstrap Modal中下拉問題的解決方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-09-09JavaScript 繼承 封裝 多態(tài)實現及原理詳解
這篇文章主要介紹了JavaScript 繼承 封裝 多態(tài)實現及原理詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-07-07