electron 应用更新
修复开发模式下升级无法使用的 Bug
在根目录创建 dev-app-update.yml:
yml
provider: generic
url: https://releases.xxx.com/test/client/win/x64/1
2
2
升级逻辑加以下代码:
ts
import path from 'path';
import fs from 'fs';
if (process.env.NODE_ENV === 'development') {
const devUpdateConfigPath = path.join(__dirname, 'dev-app-update.yml');
Object.defineProperty(app, 'isPackaged', {
get() {
return true;
},
});
if (fs.existsSync(devUpdateConfigPath)) {
autoUpdater.updateConfigPath = devUpdateConfigPath;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
electron 应用更新-基于github
(1)自动发布release版本
配置发布命令
https://www.electron.build/configuration/publish#how-to-publish
json
"build": "vue-tsc && vite build && electron-builder -c electron.config.json",
"release": "vue-tsc && vite build && electron-builder -c electron.config.json",1
2
2
生成token
https://github.com/settings/tokens/new
修改配置
json
"publish": {
"provider": "github",
"token": "",
"owner": "",
"private": true,
"releaseType": "draft", // “draft” | “prerelease” | “release” | “undefined” 默认草稿状态
"repo": "electron-vue-application"
},1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
(2)自动更新逻辑修改
安装electron-updater: https://www.electron.build/auto-update
相比 autoUpdater,第三方包 electron-updater 有以下优势:
- 不需要搭建专门的更新服务(如 Hazel、Nuts 等)。
- 同时支持 macOS 和 Windows 签名。
- 支持获取下载进度,等等。
具体代码实现
主进程封装更新模块
ts
import { dialog } from 'electron'
import { autoUpdater } from 'electron-updater'
import { join, dirname } from 'node:path'
import { logger } from '../../modules/logger.ts'
import { ipcMainService } from '../../ipcManager'
class AppAutoUpdate {
initConfig() {
if (process.env.NODE_ENV === 'development') {
// 需要注意这里的路径问题
autoUpdater.updateConfigPath = join(__dirname, `../../dev-update.yml`);
}
autoUpdater.autoDownload = false;
autoUpdater.logger = logger
autoUpdater.forceDevUpdateConfig = true
// events
autoUpdater.on('error', (error) => {
// 若github上都没有最新的release可用就会报错,需要将 draft 状态的更改为 release 状态
dialog.showErrorBox('更新失败', JSON.stringify(error))
})
autoUpdater.on('update-available', () => {
ipcMainService.send('app:update-info', {
type: 'update-available'
})
})
autoUpdater.on('checking-for-update', () => {
console.log('checking-for-update: ')
})
autoUpdater.on('update-not-available', () => {
ipcMainService.send('app:update-info', {
type: 'update-not-available',
data: {
version: autoUpdater.currentVersion,
}
})
})
autoUpdater.on('download-progress', (progressObj) => {
// 通知渲染进程下载进度
ipcMainService.send('app:update-info', {
type: 'download-progress',
data: progressObj
})
})
autoUpdater.on('update-downloaded', () => {
// dialog.showMessageBox({
// type: 'info',
// title: '安装更新',
// message: `更新下载完毕,应用将重启并安装`,
// buttons: ['确定']
// detail: '',
// }).then(res => {
// autoUpdater.quitAndInstall()
// })
ipcMainService.send('app:update-info', {
type: 'update-downloaded'
})
})
this.registerAppUpdateEventHandler()
}
checkAppUpdate() {
autoUpdater.checkForUpdates();
}
handleDownloadUpdate() {
autoUpdater.downloadUpdate()
}
handleQuitAppToInstall() {
autoUpdater.quitAndInstall()
}
registerAppUpdateEventHandler() {
// 检查是否有可用版本
ipcMainService.on('app:check-update', (event: any, data) => {
this.checkAppUpdate()
})
ipcMainService.on('app:update-download', (event: any, data) => {
this.handleDownloadUpdate()
})
ipcMainService.on('app:update-install', (event: any, data) => {
this.handleQuitAppToInstall()
})
}
}
export const appAutoUpdate = new AppAutoUpdate()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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
渲染进程逻辑修改
js
<template>
<div>
<CommonBar />
<div class="update-container">
<span>下载速度: {{ downloadSpeed }} / S</span>
<el-progress :text-inside="true" :stroke-width="26" :percentage="percentage" />
<div class="flex">
<el-button @click="checkForUpdate()">检查更新</el-button>
</div>
</div>
</div>
</template>
<script setup lang='ts'>
import { ref, onMounted } from 'vue'
import { ElMessageBox } from 'element-plus'
import { ipcRenderService } from '@/render/services/ipcService'
import CommonBar from '@/render/components/base/commonBar/index.vue'
import { convertBandwidth } from '@/render/utils/format'
const percentage = ref(0)
const downloadSpeed = ref('0 KB')
function checkForUpdate() {
ipcRenderService.send('app:check-update')
}
onMounted(() => {
// 发现可用更新
ipcRenderService.on('app:update-info', (event: any, data: any) => {
console.log('data: app:update-info', data);
switch (data.type) {
case 'update-available':
ElMessageBox.confirm(
'发现可用更新,是否立即下载?',
'更新提示',
{
confirmButtonText: '立即下载',
cancelButtonText: '取消',
type: 'info',
center: true,
}
).then(() => {
ipcRenderService.send('app:update-download')
})
.catch(() => {
})
break;
case 'update-downloaded':
ElMessageBox.confirm(
'更新下载完毕,是否重启并安装?',
'更新提示',
{
confirmButtonText: '立即安装',
cancelButtonText: '取消',
type: 'info',
center: true,
}
).then(() => {
ipcRenderService.send('app:update-install')
})
.catch(() => {
})
break;
case 'download-progress':
percentage.value = parseFloat(data.data.percent.toFixed(2));
downloadSpeed.value = convertBandwidth(data.data.bytesPerSecond) || '0 KB';
break
case 'update-not-available':
ElMessageBox.confirm(
'当前已是最新版本',
'更新提示',
{
confirmButtonText: '确定',
showCancelButton: false,
type: 'info',
center: true,
}
).then(() => {
})
.catch(() => {
})
break
default:
break;
}
});
})
</script>
<style lang="scss" scoped>
:deep(.ep-progress-bar__inner) {
background: linear-gradient(to right, #009aff, #7a00ff);
}
.update-container {
display: flex;
justify-content: center;
flex-direction: column;
padding: 50px 50px 0;
span {
font-size: 13px
}
};
.flex {
padding: 50px;
display: flex;
justify-content: center;
align-items: center;
}
</style>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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
