electron-vue构建桌面客户端使用electron-updater实现在线更新

公司项目是使用elctron-vue构建的,之前一直是打包成一个绿色免安装版给用户使用的,也没有在线更新的功能,每次更新了新的版本用户可能都不知道。为了提升用户体验,最近打算把在线更新这个功能解决了。

公司项目是使用elctron-vue构建的,之前一直是打包成一个绿色免安装版给用户使用的,也没有在线更新的功能,每次更新了新的版本用户可能都不知道。

为了提升用户体验,最近打算把在线更新这个功能解决了。

这两天研究了下,基本已经实现,现在对实现做一些总结:

现在实现了window端在线下载安装更新,Mac端由于没有证书。。目前是检测到有更新后,跳转到浏览器下载新的安装包。

使用electron-builder打包

在开始使用electron-vue构建项目的时候,记得选用electron-builder

electron-vue集成了两种打包方式:electron-packager和electron-builder

他们的区别是:(应该是这些区别吧)

  1. electron-packager打包成的是绿色安装包,没有实现自动更新功能;
  2. electron-builder打包好的是安装文件,配合插件实现了自动更新。

所以我们要实现自动更新,记得选用electron-builder打包的方式。

之前项目是使用的electron-packager构建方式,在改成electron-builder的没有成功。。。最后只有重新构建了项目,把现有的代码复制过去了(据同事说他弄成功了?????excuse me?!)

安装electron-builder模块

  1. 安装模块
1
npm install --save electron-builder
  1. 配置package.json

为了打包时生成latest.yml文件,需要在 build 参数中添加 publish 配置。

其他配置,请阅读官方文档

1
2
3
4
5
6
7
8
9
10
11
12
13
"build": {
"publish": [
{
"provider": "generic",
"url": "http://**.**.**.**:3002/download/",//更新服务器地址,可为空
}
],
...,
"nsis":{

}
}

注意:配置了publish才会生成latest.yml(mac环境是生成的latest-mac.yml)文件,用于自动更新的配置信息;latest.yml文件是打包过程生成的文件,为避免自动更新出错,打包后禁止对latest.yml文件做任何修改。如果文件有误,必须重新打包获取新的latest.yml文件!!!

  1. 增加nsis配置(可省略)

nsis配置不会影响自动更新功能,但是可以优化用户体验,比如是否允许用户自定义安装位置、是否添加桌面快捷方式、安装完成是否立即启动、配置安装图标等。nsis 配置也是添加在 build 参数中。 详细参数配置可参见官方文档:nsis配置

1
2
3
4
5
6
7
8
9
10
"nsis": {
"oneClick": true,
"perMachine": true,
"allowElevation": true,
"allowToChangeInstallationDirectory": true,
"createDesktopShortcut": true,
"runAfterFinish": true,
"installerIcon": "./build/icon.ico",
"uninstallerIcon": "./build/icon.ico"
},

4.配置主进程main.js文件(或主进程main中的index.js文件),引入 electron-updater 文件,添加自动更新检测和事件监听:

这里有两种情况(按你自己情况自己选择,还可以有其他的方式,这里列两种比较常见的更新方式):

1.检测到更新后,会立即下载,然后退出应用或者点击事件会触发更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import { app, BrowserWindow, ipcMain } from 'electron'

// 注意这个autoUpdater不是electron中的autoUpdater
import { autoUpdater } from "electron-updater"
// 更新服务器地址,比如"http://**.**.**.**:3002/download/"
let uploadUrl = 'http://xxxx/download/' //和package.json中build -> publish中的url对应

// 检测更新,在你想要检查更新的时候执行,renderer事件触发后的操作自行编写
function updateHandle() {
let message = {
error: '检查更新出错',
checking: '正在检查更新……',
updateAva: '检测到新版本,正在下载……',
updateNotAva: '现在使用的就是最新版本,不用更新',
};
const os = require('os');

autoUpdater.setFeedURL(uploadUrl);
autoUpdater.on('error', function (error) {
sendUpdateMessage(message.error)
});
autoUpdater.on('checking-for-update', function () {
sendUpdateMessage(message.checking)
});
autoUpdater.on('update-available', function (info) {
sendUpdateMessage(message.updateAva)
});
autoUpdater.on('update-not-available', function (info) {
sendUpdateMessage(message.updateNotAva)
});

// 更新下载进度事件
autoUpdater.on('download-progress', function (progressObj) {
mainWindow.webContents.send('downloadProgress', progressObj)
})
autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {

ipcMain.on('isUpdateNow', (e, arg) => {
console.log(arguments);
console.log("开始更新");
//some code here to handle event
autoUpdater.quitAndInstall();
});

mainWindow.webContents.send('isUpdateNow')
});

ipcMain.on("checkForUpdate",()=>{
//执行自动更新检查
autoUpdater.checkForUpdates();
})
}

// 通过main进程发送事件给renderer进程,提示更新信息
function sendUpdateMessage(text) {
mainWindow.webContents.send('message', text)
}

2. 检测到更新应用,手动触发下载,并更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//检查更新
function checkForUpdates(){
autoUpdater.autoInstallOnAppQuit = false // 阻止退出自动更新
autoUpdater.autoDownload = false // 阻止自动下载
let message = {
error: '检查更新出错',
checking: '正在检查更新……',
updateAva: '检测到新版本,正在下载……',
updateNotAva: '现在使用的就是最新版本,不用更新',
};
autoUpdater.setFeedURL(feedURL) // 设置检测更新地址
// 下面是自动更新的整个生命周期所发生的事件
sendUpdateMessage('currentVersion', autoUpdater.currentVersion.version); // 获取当前应用的版本号
autoUpdater.on('error', function (error) {
sendUpdateMessage('error', message.error);
});
autoUpdater.on('checking-for-update', function () {
sendUpdateMessage('checking-for-update', message.checking);
});
autoUpdater.on('update-available', function (info) {
sendUpdateMessage('newVersion', info);
sendUpdateMessage('update-available', message.updateAva);
});
autoUpdater.on('update-not-available', function (info) {
sendUpdateMessage('notAvailable', info);
sendUpdateMessage('update-not-available', message.updateNotAva);
});
autoUpdater.checkForUpdates() // 检测是否有新版本

}
// 手动触发下载
ipcMain.on("downLoadAndUpdate", () => {
autoUpdater.autoDownload = true // 开启自动下载
// 更新下载进度事件
autoUpdater.on('download-progress', function (progressObj) {
webContents.send('downloadProgress', progressObj)
// sendUpdateMessage('downloadProgress', progressObj);
});
//下载完后执行
autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateURL) {
sendUpdateMessage('isUpdateNow',"下载完毕,即将更新...")
setTimeout(() => {
autoUpdater.quitAndInstall();
},1500)

})
autoUpdater.checkForUpdates()
})
// 登录成功后检测更新
ipcMain.on("checkNewVersion", () => {
checkForUpdates()
})

// 主进程主动发送消息给渲染进程函数
function sendUpdateMessage(message, data) {
webContents.send('message', {message, data});
}

  1. 在视图层做处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import {ipcRenderer} from 'electron'

    ipcRenderer.on("message", (event, text) => {
    // 这里来处理自动更新的生命周期中的一些事件
    console.log('text=',text)

    });
    // 下载进度
    ipcRenderer.on("downloadProgress", (event, progressObj) => {
    this.downloadPercent = parseInt(progressObj.percent) || 0;
    });
  2. 项目打包

将新版本latest.yml文件和exe文件(MAC下将latest-mac.yml,zip和dmg文件)放到package.json中build -> publish中的url对应的地址下

注意:mac下需要有证书才能进自动更新,但没有证书也能打包成功

mac打包签名可以看下这篇文章:Electron 打包Mac安装包代码签名问题解决方案

7.软件升级版本,修改package.json中的version属性

至此整个打包自动更新的主要流程完成了

如有疑问,可在本文留言,看到会第一事件回复。

感谢阅读