前言
Electron幫助我們突破瀏覽器的界限,通過(guò)Electron構(gòu)建的桌面應(yīng)用擁有各種瀏覽器應(yīng)用夢(mèng)寐以求的能力。
Electron提供的autoUpdater還可以幫助我們實(shí)現(xiàn)桌面應(yīng)用的自動(dòng)更新。
文件結(jié)構(gòu)
首先,我們已經(jīng)有了一個(gè)基于Electron做的應(yīng)用,項(xiàng)目中有兩個(gè)package.json。這樣做的一個(gè)原因是將devDependencies和dependencies分開(kāi)了,另外就是不需要在打包的時(shí)候再去指定哪些依賴不需要一起打到安裝包里面去了(通過(guò)ignore參數(shù))。
目錄結(jié)構(gòu)類似于這樣:
myapp
-node_modules
-package.json
-app
-js
-css
-index.html
-main.js
-package.json
外面的package.json內(nèi)容類似于:
{
"name": "myapp",
"main": "app/main.js",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron-prebuilt": "^1.2.7"
}
}
里面的package.json的內(nèi)容類似于:
{
"name": "myapp",
"version": "1.0",
"main": "main.js",
"description": "my app",
"scripts": {
"start": "electron ."
},
"dependencies": {}
}
注意里面的package.json中的name,version,description是必填的,接下來(lái)打包會(huì)用到。
electron-squirrel-startup
為了使最后的安裝包能夠?qū)崿F(xiàn)自動(dòng)更新,我們需要對(duì)現(xiàn)有的應(yīng)用做一些改動(dòng),使它可以處理一些啟動(dòng)或者安裝時(shí)的事件。
我們可以在main.js里面加入一些處理的代碼或者方便起見(jiàn),我們可以直接使用electron-squirrel-startup。
先安裝:
npm install electron-squirrel-startup --save
因?yàn)樾枰?code>main.js里面用到,我們需要將其安裝在app里面。
在main.js里面使用它,第一行加入如下代碼即可:
if (require('electron-squirrel-startup')) return;
有興趣的童鞋可以一起跟我去看看electron-squirrel-startup做了什么事情,急著打包的童鞋可以直接忽略這一段:
在myapp/app/node_modules/electron-squirrel-startup下面有一個(gè)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();
它的代碼只有短短幾十行,做的事情也很簡(jiǎn)單,注意返回值為true的那幾行,基本上來(lái)說(shuō)就是安裝時(shí),更新完成時(shí),卸載時(shí) main.js都會(huì)被調(diào)用,我們需要根據(jù)不同的情況做不同的事情,完成這些事情后不要啟動(dòng)應(yīng)用(會(huì)出錯(cuò)),直接退出就好。
正常啟動(dòng)前我們需要去檢測(cè)是否有新的安裝包,之后下載新包,重新安裝,重啟應(yīng)用,為了做到這一點(diǎn),我們需要在main.js里面加入如下代碼:
app.on('ready', () => {
//安裝后第一次啟動(dòng)不去檢測(cè)更新,go做的事情就是啟動(dòng)我們的應(yīng)用
if (process.argv[1] == '--squirrel-firstrun') {
go();
return;
}
/* 設(shè)置自動(dòng)更新的feedURL,本地測(cè)試可以設(shè)置為類似于http://127.0.0.1:8080/latest
* 在latest文件夾下放著三個(gè)我們的安裝文件(Setup.exe,RELEASES,myapp-1.0-full.nupkg),下面會(huì)講到
* /
autoUpdater.setFeedURL(feedURL);
autoUpdater.on('update-downloaded', function() {
// 下載完成,更新前端顯示
autoUpdater.quitAndInstall();
});
try {
// 不是安裝應(yīng)用的情況下啟動(dòng)下回出錯(cuò),此時(shí)直接正常啟動(dòng)應(yīng)用
autoUpdater.checkForUpdates();
} catch (ex) {
go();
return;
}
// createWindow是我們自己定義的方法,用來(lái)創(chuàng)建窗口,此處用來(lái)創(chuàng)建檢測(cè)更新的窗口
createWindow({
name: 'updateWindow',
url: 'check-for-updates.html',
title: "checkForUpdates",
icon: icon,
frame: false,
width: 1306,
height: 750
});
});
自動(dòng)更新后臺(tái)搭建
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("應(yīng)用實(shí)例,訪問(wèn)地址為 http://%s:%s", host, port);
});
文件結(jié)構(gòu)如下:
autoupdate-backend
-package.json
-index.js
-node_modules
-releases
-latest
此時(shí)latest文件夾里面還是空的,之后我們開(kāi)始打包,將打包出來(lái)的三個(gè)文件放在此處即可。
electron-packager
在myapp下安裝:
npm install electron-packager --save-dev
npm install electron-packager -g
兩種安裝方式對(duì)應(yīng)兩種使用方式,第一種在腳本中使用,第二種的命令行使用。
腳本中使用,小姐姐在這里借助了gulp,所以需要安裝gulp:
npm install gulp --save-dev
npm install gulp -g
新建GulpFile.js,定義一個(gè)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,
/*桌面快捷方式名稱以及開(kāi)始菜單文件夾名稱*/
'version-string': {
CompanyName: 'MyCompany Inc.',
ProductName: 'myapp'
}
}, function (err) {
if (err) {
console.log(err);
} else {
callback && callback();
}
});
}
需要打包的時(shí)候,打開(kāi)命令行:
gulp generate-package
這樣做的好處是調(diào)用方便,當(dāng)然我們也可以直接通過(guò)命令行調(diào)用electron-packager,前提是我們?nèi)职惭b了它或者將其安裝目錄添加到了環(huán)境變量中:
electron-package ./app --platform=win32 --arch=ia32 --icon="app/favicon.ico" --out="productin/package" --version-string.CompanyName="MyCompany Inc." --version-string.ProductName="myapp"
更多參數(shù)一一加上即可。
貼上官方文檔鏈接:
github鏈接:https://github.com/electron-userland/electron-packager
下面兩個(gè)鏈接在上面的文檔里面都能找到,但是個(gè)人感覺(jué)比較常用,還是貼出來(lái):
參數(shù)使用: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里面添加一個(gè)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,
/*桌面快捷方式名稱以及開(kāi)始菜單文件夾名稱*/
'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í)行任務(wù):
gulp generate-installer
第一次會(huì)非常慢,但是執(zhí)行完成后我們的安裝包就出來(lái)了。
此時(shí)我們的文件結(jié)構(gòu)是:
myapp
-GulpFile.js
-package.json
-node_modules
-app
-production
-package
-myapp-win32-ia32
- 各種文件,包含一個(gè)myapp.exe,雙擊可以直接運(yùn)行
-installer
-myapp-win32-ia32
-Setup.exe
-RELEASES
-myapp-1.0-full.nupkg
有了三個(gè)文件,將他們粘到之前的autoupdate-backend/releases/latest文件夾下面。
測(cè)試
- 啟動(dòng)我們的自動(dòng)更新后臺(tái);
- 將
myapp/app下的package.json里面的version改為1.1,再次打包; - 將之前的
autoupdate-backend中的latest文件夾重命名為1.0; - 新建文件夾
latest,將新打包產(chǎn)生的三個(gè)文件粘進(jìn)去; - 雙擊1.0里面的
Setup.exe安裝應(yīng)用; - 關(guān)閉應(yīng)用,雙擊桌面上的快捷方式
myapp.exe再次打開(kāi)應(yīng)用;
不出意外此時(shí)就會(huì)去進(jìn)行自動(dòng)更新的操作,結(jié)束后自動(dòng)重啟,再次打開(kāi)時(shí)已經(jīng)是1.1的應(yīng)用。
以上,謝謝查看,有錯(cuò)誤或者不足的地方請(qǐng)不吝指正,微笑臉~