electron创建应用、打包、自动更新
安装环境
- 安装node npm cnpm tomcat
- 安装 visual studio 2017
- 安装 python27
- 安装 Squirrel.Windows
git bush中执行:
git clone --recursive https://github.com/squirrel/squirrel.windows
cd squirrel.windows
…NuGet\NuGet.exe restore
msbuild /p:Configuration=Release - 参考官网教程:
打造你的第一个 Electron 应用
构建步骤(Windows)
autoUpdater
创建应用
- 新建文件夹znyts,里面新建几个文件
//package.json
{
"name": "znyts",
"version": "1.0.0",
"main": "main.js",
"author": "lyt",
"description": "a demo create by lyt",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron": "^4.1.0"
}
}
//main.js
const { app, BrowserWindow } = require('electron')
// 保持对window对象的全局引用,如果不这么做的话,当JavaScript对象被
// 垃圾回收的时候,window对象将会自动的关闭
let win
function createWindow () {
// 创建浏览器窗口。
win = new BrowserWindow({ width: 800, height: 600 })
// 然后加载应用的 index.html。
win.loadFile('index.html')
// win.loadURL('http://localhost:8080')
// 打开开发者工具
win.webContents.openDevTools()
// 当 window 被关闭,这个事件会被触发。
win.on('closed', () => {
// 取消引用 window 对象,如果你的应用支持多窗口的话,
// 通常会把多个 window 对象存放在一个数组里面,
// 与此同时,你应该删除相应的元素。
win = null
})
}
// Electron 会在初始化后并准备
// 创建浏览器窗口时,调用这个函数。
// 部分 API 在 ready 事件触发后才能使用。
app.on('ready', createWindow)
// 当全部窗口关闭时退出。
app.on('window-all-closed', () => {
// 在 macOS 上,除非用户用 Cmd + Q 确定地退出,
// 否则绝大部分应用及其菜单栏会保持**。
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
// 在macOS上,当单击dock图标并且没有其他窗口打开时,
// 通常在应用程序中重新创建一个窗口。
if (win === null) {
createWindow()
}
})
// 在这个文件中,你可以续写应用剩下主进程代码。
// 也可以拆分成几个文件,然后用 require 导入。
//index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
<style>
body,html{
margin:0;padding:0;
}
</style>
</head>
<body>
<div>第一个electron 项目</div>
</body>
</html>
-
cnpm i
引入依赖 -
npm start
运行项目
至此项目启动成功,如图所示
打包更新
- 安装依赖
cnpm i -g grunt-cli
cnpm i --save-dev electron-packager grunt grunt-electron-installer
cnpm i --save child_process electron-squirrel-startup os
在package.json
的scripts中添加几个命令,最终package.json
变化如下
{
"name": "znyts",
"version": "1.0.0",
"main": "main.js",
"author": "lyt",
"description": "a demo create by lyt",
"scripts": {
"start": "electron .",
"package": "electron-packager . znyts --asar --win --out target --overwrite",
"package32": "electron-packager . znyts --asar --platform=win32 --arch=ia32 --out target --overwrite",
"package64": "electron-packager . znyts --asar --platform=win32 --arch=x64 --out target --overwrite --icon=favicon.ico"
},
"devDependencies": {
"electron": "^4.1.0",
"electron-packager": "^13.1.1",
"grunt": "^1.0.3",
"grunt-electron-installer": "^2.1.0"
},
"dependencies": {
"child_process": "^1.0.2",
"electron-squirrel-startup": "^1.0.0",
"os": "^0.1.1"
}
}
- 创建几个文件
//Gruntfile.js
var grunt=require('grunt');
//配置
grunt.config.init({
pkg: grunt.file.readJSON('gruntPackage.json'),
'create-windows-installer': {
x64:{
version:'1.0.0',
authors:'lyt',
projectUrl:'',
appDirectory:'./target/znyts-win32-x64',//要打包的输入目录
outputDirectory:'./output',//grunt打包后的输出目录
exe:'znyts.exe',
title:"某项目",//项目中文名
description:'某项目',
setupIcon:"favicon.ico",//可以到[http://www.faviconico.org/favicon](http://www.faviconico.org/favicon)生成48X48的ico图标
loadingGif:"loading.gif",//安装应用时的动画,去百度随便找个gif
noMsi:true
}
}
});
//加载任务
grunt.loadNpmTasks('grunt-electron-installer');
//设置为默认
grunt.registerTask('default', ['create-windows-installer']);
//gruntPackage.json
{
"name": "znyts",
"version": "1.0.0",
"devDependencies": {
"grunt": "^1.2.0",
"grunt-electron-installer": "^2.1.0"
}
}
- 修改几个文件
//main.js底部添加
const electron = require('electron')
//自动更新
const autoUpdater = electron.autoUpdater
function startupEventHandle(){
if(require('electron-squirrel-startup')) return;
var handleStartupEvent = function () {
if (process.platform !== 'win32') {
return false;
}
var squirrelCommand = process.argv[1];
switch (squirrelCommand) {
case '--squirrel-install':
case '--squirrel-updated':
install();
return true;
case '--squirrel-uninstall':
uninstall();
app.quit();
return true;
case '--squirrel-obsolete':
app.quit();
return true;
}
// 安装
function install() {
var cp = require('child_process');
var updateDotExe = path.resolve(path.dirname(process.execPath), '..', 'update.exe');
var target = path.basename(process.execPath);
var child = cp.spawn(updateDotExe, ["--createShortcut", target], { detached: true });
child.on('close', function(code) {
app.quit();
});
}
// 卸载
function uninstall() {
var cp = require('child_process');
var updateDotExe = path.resolve(path.dirname(process.execPath), '..', 'update.exe');
var target = path.basename(process.execPath);
var child = cp.spawn(updateDotExe, ["--removeShortcut", target], { detached: true });
child.on('close', function(code) {
app.quit();
});
}
};
if (handleStartupEvent()) {
return ;
}
}
function updateHandle(){
const { ipcMain } = require('electron')
ipcMain.on('check-for-update', function(event, arg) {
let appName='某项目更新';
let appIcon=__dirname + '/favicon.ico';
let message={
error:'检查更新出错',
checking:'正在检查更新……',
updateAva:'下载更新成功',
updateNotAva:'现在使用的就是最新版本,不用更新',
downloaded:'最新版本已下载,将在重启程序后更新'
};
const os = require('os');
const {dialog} = require('electron');
autoUpdater.setFeedURL('http://localhost:8080/znyts/');//放最新版本文件的文件夹的服务器地址
autoUpdater.on('error', function(error){
return dialog.showMessageBox(win, {
type: 'info',
icon: appIcon,
buttons: ['下一步'],
title: appName,
message: message.error,
detail: '\r'+error
});
})
.on('checking-for-update', function(e) {
return dialog.showMessageBox(win, {
type: 'info',
icon: appIcon,
buttons: ['下一步'],
title: appName,
message: message.checking
});
})
.on('update-available', function(e) {
var downloadConfirmation = dialog.showMessageBox(win, {
type: 'info',
icon: appIcon,
buttons: ['下一步'],
title: appName,
message: message.updateAva
});
if (downloadConfirmation === 0) {
return;
}
})
.on('update-not-available', function(e) {
return dialog.showMessageBox(win, {
type: 'info',
icon: appIcon,
buttons: ['下一步'],
title: appName,
message: message.updateNotAva
});
})
.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
var index = dialog.showMessageBox(win, {
type: 'info',
icon: appIcon,
buttons: ['现在重启此程序','稍后重启此程序'],
title: appName,
message: message.downloaded,
detail: releaseName + "\n\n" + releaseNotes
});
if (index === 1) return;
autoUpdater.quitAndInstall();
});
autoUpdater.checkForUpdates();
});
}
startupEventHandle()
updateHandle()
//index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
<style>
body,html{
margin:0;padding:0;
}
</style>
</head>
<body>
<div>第一个electron 项目</div>
<button id="update">update</button>
<script>
//render请求main的数据
const { ipcRenderer } = require('electron')
ipcRenderer.send('asynchronous-message', 'ping');
update.onclick=function(){
ipcRenderer.send('check-for-update');
}
// console.log("更新成功");
</script>
</body>
</html>
- 执行
npm run package64
打包, 生成的文件在target
中,点击znyts.exe
就可以运行该项目,但没有快捷方式和自动更新。目录如图: - 执行
grunt
打出 安装包 ,生成output
文件夹如下: - 双击Setup.exe即可安装并生成桌面快捷方式。安装成功后,可在程序与功能中卸载,如图
- 自动更新:
将gruntPackage.json
package.json
Gruntfile.js
里面的version:1.0.0 改成1.0.2
将index.html
中的// console.log("更新成功");
注释打开 - 执行第4步,第5步得到新的output文件夹,注意不要执行第6步了。
- 使用iis 或tomcat 部署服务器并可以从
http://localhost:8080/znyts/
获取到新的内容。
例如我安装并启动了tomcat服务器,默认端口号8080, 将新的output里面的内容放到tomat中,目录结构如下: - 运行
znyts
程序并点击update按钮,一直下一步即可更新成功,如图:
更新成功之后运行,会在控制台打印出更新成功
- 自动更新原理
1、根据设置的setFeedURL路径,UpdateManager检查路径下的RELEASES文件,与安装目录下的RELEASES文件进行比对。
2、如果出现版本不同的情况,将下载路径中的更新包,下载完成后自动进行更新(包括修改快捷方式和清除旧版本)。
3、更新成功后,会保留上一个版本,并清除上一个版本之前的所有版本。
参考
https://segmentfault.com/a/1190000008287730
https://segmentfault.com/a/1190000007616641
源码下载
(ps:欢迎交流,会及时回复的。觉得写的好的,点个赞吧+_+。)