需求:开发桌面端一体机应用:先把架构搭好就会事半功倍。

新建项目

1
vue create demo

安装electron

1
yarn add electron

添加打包工具

1
vue add electron-builder

pPErlJx.png
pPEruw9.png

安装后src下会生成background.js文件

package.json也会生成对应的命令

并把background.js作为入口文件

1
2
"electron:build": "vue-cli-service electron:build",
"electron:serve": "vue-cli-service electron:serve",

打包时遇到的error ERR_ELECTRON_BUILDER_CANNOT_EXECUTE

解决方法

1、下载 与当前elecron本版一致的 win32-x64 然后放到 参考路径 : C:\Users\Admin/AppData\Local\electron\Cache
2 、下载 对应的 winCodeSign 解压后 放到 C:\User\Admin/AppData\Local\electron-builder\Cache 没有 winCodeSign 文件夹 自己新建
3、 下载对应的 nsis 和 对应的 nsis-resources 放到 C:\User\Admin/AppData\Local\electron-builder\Cache/nsis 下面

注:复制错误提示里面的链接下载
比如下面的错 :复制https://github.com/electron/electron/releases/download/v20.0.0/electron-v20.0.0-win32-x64.zip进行下载 然后执行上面1的步骤即可解决。

PS:这里要注意的是可能有版本相关的问题(我有两个项目,两个项目下载的版本不一样。一个13.6.9一个20)
pPErQF1.png

background.js

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
'use strict'

import {
app,
protocol,
BrowserWindow,
Menu
} from 'electron'
import {
createProtocol
} from 'vue-cli-plugin-electron-builder/lib'


import installExtension, {
VUEJS_DEVTOOLS
} from 'electron-devtools-installer'

const isDevelopment = process.env.NODE_ENV !== 'production'

// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([{
scheme: 'app',
privileges: {
secure: true,
standard: true
}
}])

async function createWindow() {
// Create the browser window.
Menu.setApplicationMenu(null) //隐藏菜单栏
const win = new BrowserWindow({
// width: 1900, //最大宽高 不设置则自适应
// height: 1600,
//fullscreen: true,//默认全屏
show: false,
webPreferences: {
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION
}
})
win.maximize();//窗口最大化
win.show();
// win.webContents.openDevTools()
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
if (!process.env.IS_TEST) win.webContents.openDevTools() //调试工具
} else {
createProtocol('app')
// Load the index.html when not in development
win.loadURL('app://./index.html')
}
}

// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})

//点击关闭
app.on('quit', () => {
app.quit()
})

app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
if (isDevelopment && !process.env.IS_TEST) {
// Install Vue Devtools
try {
await installExtension(VUEJS_DEVTOOLS)
} catch (e) {
console.error('Vue Devtools failed to install:', e.toString())
}
}
createWindow()
})

// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
if (process.platform === 'win32') {
process.on('message', (data) => {
if (data === 'graceful-exit') {
app.quit()
}
})
} else {
process.on('SIGTERM', () => {
app.quit()
})
}
}

运行 electron:serve 命令就可以启动成功了。

打包运行 electron:build命令

更多配置参考 electron官方文档

自动更新 electron-updater

1、安装依赖 yarn add electron-updater

2、自定义更新的文件 handleUpdate.js

注: 打包完会生成一个latest.yml文件,把这个文件和生成的.exe文件放在服务器上,会自动检测版本

pPEreL4.png

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
import {
autoUpdater
} from 'electron-updater'

import {
ipcMain,dialog
} from 'electron'
let mainWindow = null;
export function handleUpdate(window, feedUrl) {
mainWindow = window;
let message = {
error: '检查更新出错',
checking: '正在检查更新……',
updateAva: '检测到新版本,正在下载……',
updateNotAva: '现在使用的就是最新版本,不用更新',
};

`autoUpdater.autoDownload = false;` //取消自动下载
//设置更新包的地址
autoUpdater.setFeedURL(feedUrl);
//监听升级失败事件
autoUpdater.on('error', function(error) {
sendUpdateMessage({
cmd: 'error',
message: error
})
});
//监听开始检测更新事件
autoUpdater.on('checking-for-update', function(message) {
sendUpdateMessage({
cmd: 'checking-for-update',
message: message
})
});
//监听发现可用更新事件
autoUpdater.on('update-available', function(message) {
sendUpdateMessage({
cmd: 'update-available',
message: message
})
//新加内容
/** `const options = {
type: 'info',
buttons: ['确定', '取消'],
title: '更新提示',
message: '发现有新版本,是否更新?',
cancelId: 1
}
dialog.showMessageBox(options).then(res => {
if (res.response === 0) {
sendUpdateMessage({
cmd: 'confimUpdate',
message: message
})
autoUpdater.downloadUpdate()
} else {
return;
}
})`*/




});
//监听没有可用更新事件
autoUpdater.on('update-not-available', function(message) {
sendUpdateMessage({
cmd: 'update-not-available',
message: message
})
});

// 更新下载进度事件
autoUpdater.on('download-progress', function(progressObj) {
sendUpdateMessage({
cmd: 'download-progress',
message: progressObj
})
});
//监听下载完成事件
autoUpdater.on('update-downloaded', function(event, releaseNotes, releaseName, releaseDate, updateUrl) {
sendUpdateMessage({
cmd: 'update-downloaded',
message: {
releaseNotes,
releaseName,
releaseDate,
updateUrl
}
})
//退出并安装更新包
//autoUpdater.quitAndInstall();
});
//新增
ipcMain.on("quit-install", (e, arg) => {
autoUpdater.quitAndInstall();
})
//接收渲染进程消息,开始检查更新
ipcMain.on("checkForUpdate", (e, arg) => {
//执行自动更新检查
// sendUpdateMessage({cmd:'checkForUpdate',message:arg})
autoUpdater.checkForUpdates();
})
}
//给渲染进程发送消息
function sendUpdateMessage(text) {
mainWindow.webContents.send('message', text)
}

3、在主进程文件background.js里面引用 handleUpdate.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
`const {
handleUpdate
} = require('./handleUpdate.js') //根据自己路径引入 我是跟background.js同级放的`

if (process.env.WEBPACK_DEV_SERVER_URL) {
await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
if (!process.env.IS_TEST) win.webContents.openDevTools() //调试工具
} else {
createProtocol('app')
win.loadURL('app://./index.html')
}

`//检查更新
handleUpdate(win, ".exe文件存放路径")`

在app.vue里面渲染更新进度样式

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
<template>
<div id="app">
<MainPage></MainPage>
//2023/3/21 根据user要求改为静默式更新 只提示用户安装即可
<!-- <el-dialog title="正在更新新版本,请稍候..." :visible.sync="dialogVisible" width="50%"
:close-on-click-modal="closeOnClickModal" :close-on-press-escape="closeOnPressEscape"
:show-close="showClose" center>
<div style="width:100%;height:50vh;line-height:50vh;text-align:center">
<el-progress :stroke-width="26" :percentage="percentage" :width="strokeWidth" :show-text="true"
type="circle"></el-progress>
</div>
</el-dialog>-->
</div>
</template>
<script>
let ipcRenderer = require("electron").ipcRenderer;
import MainPage from './views/MainPage.vue'
export default {
components: {
MainPage
},
data() {
return {
dialogVisible: false,
closeOnClickModal: false,
closeOnPressEscape: false,
showClose: false,
percentage: 0,
strokeWidth: 400,
timer:0
}
},
created() {

},
unmounted() {
clearInterval(this.timer)
},
mounted() {
let _this = this;
//新增
this.timer = setInterval(() => {
setTimeout(() => {
ipcRenderer.send("checkForUpdate");
}, 1)
}, 60000 * 60 * 2)

//接收主进程版本更新消息
ipcRenderer.on("message", (event, arg) => {
//新增内容
/**`if ("confimUpdate" == arg.cmd) {
_this.dialogVisible = true;
} else if ("download-progress" == arg.cmd) {
console.log(arg.message.percent);
let percent = Math.round(parseFloat(arg.message.percent));
_this.percentage = percent;
} else if ("error" == arg.cmd) {
_this.dialogVisible = false;
_this.$message("更新失败");
}`*/
//2023/03/21改为如下

if ("update-downloaded" == arg.cmd) {
_this.$alert('新版本已经下载完成', '提示信息', {
confirmButtonText: '立即安装',
callback: (action) => {
ipcRenderer.send("quit-install")
}
});
}


});

}

}
</script>

效果图 (没有了,不是这个效果了)
pPErneJ.png
新的效果:

pPErKoR.png

自定义安装路径

打包安装默认是直接装在c盘的

新建vue.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
module.exports = {
pluginOptions: {
electronBuilder: {
nodeIntegration: true,
builderOptions: {
nsis: {
allowToChangeInstallationDirectory: true, //自定义安装路径
oneClick: false,
},
win: {
publish: [{
provider: "generic",
url: "存放exe文件路径" //更新包地址
}],
},
productName: 'xxx' //项目名
}
},
}
}

ps: 百度时 网上好多资料都是说直接在package.json的build里面

直接加上

nsis: {
allowToChangeInstallationDirectory: true, //自定义安装路径
oneClick: false,
},

这并不适用于我的项目,所以新建了vue.config.js。

pPEr8SK.png

我这里是更新 所以直接跳过了自定义安装路径的那一步,第一次安装可以自己选择路径。

修改 项目打包安装图标

1、安装依赖 生成图标

1
yarn add electron-icon-builder

2、在package.json的script中添加命令

1
"electron:generate-icons": "electron-icon-builder --input=./public/icon.png --output=build --flatten"

3、运行 yarn electron:generate-icons 后会生成一个build文件夹(先要放一张icon.png)

pPErJyD.png

4、删除dist_electron文件夹 重新运行打包命令即可

1
yarn electron:build

5、图标已更改

pPEr1W6.png

笔记是后面写了 没有对比图 本来默认的是electron的图标

最后放一下完整的background.js文件

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
'use strict'

import {
app,
protocol,
BrowserWindow,
Menu
} from 'electron'
import {
createProtocol
} from 'vue-cli-plugin-electron-builder/lib'


import installExtension, {
VUEJS_DEVTOOLS
} from 'electron-devtools-installer'

const {
handleUpdate
} = require('./handleUpdate.js') //根据自己路径引入
const isDevelopment = process.env.NODE_ENV !== 'production'
const path = require('path')
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([{
scheme: 'app',
privileges: {
secure: true,
standard: true
}
}])

async function createWindow() {
// Create the browser window.
Menu.setApplicationMenu(null) //隐藏菜单栏
const win = new BrowserWindow({
// width: 1900, //最大宽高 不设置则自适应
// height: 1600,
//fullscreen: true,//默认全屏
show: false,
icon: path.join(__static, "../public/favicon.ico"), //标题图标
webPreferences: {
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION
}
})
win.maximize();//默认打开窗口最大化
win.show();
// win.webContents.openDevTools()
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
if (!process.env.IS_TEST) win.webContents.openDevTools() //调试工具
} else {
createProtocol('app')
// Load the index.html when not in development
win.loadURL('app://./index.html')
}

//检查更新
handleUpdate(win, ".exe文件存放路径 和vue.config.js文件里的一致")

}

// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})

app.on('quit', () => {
app.quit()
})

app.on('activate', () => {

if (BrowserWindow.getAllWindows().length === 0) createWindow()
})

app.on('ready', async () => {
if (isDevelopment && !process.env.IS_TEST) {
// Install Vue Devtools
try {
await installExtension(VUEJS_DEVTOOLS)
} catch (e) {
console.error('Vue Devtools failed to install:', e.toString())
}
}
createWindow()
})

// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
if (process.platform === 'win32') {
process.on('message', (data) => {
if (data === 'graceful-exit') {
app.quit()
}
})
} else {
process.on('SIGTERM', () => {
app.quit()
})
}
}

项目总体目录结构

pPErGQO.png
安装依赖卡的解决办法:npm config set electron_mirror https://npm.taobao.org/mirrors/electron/
后来搞了个入门案例 但是没有做更新 实现方式 electron+vite+vue3+pinia 跟上面有出入 不太影响 适合新人学习使用
项目下载