vue2+electron开发桌面端应用并实现自动更新

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

我的个人博客

新建项目

vue create demo

安装electron

yarn add electron   //可以省略 安装electron-builder时会自动安装

添加打包工具

vue add electron-builder

在这里插入图片描述
在这里插入图片描述

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

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

并把background.js作为入口文件

	"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)

在这里插入图片描述

background.js

'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,
		supportFetchAPI: true  //解决引入外部js时打包后报错Fetch API cannot load app://.ZwCloud2DSDK/ZwWasmJs.wasm.
URL
scheme
is not supported.
	}
}])

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文件放在服务器上,会自动检测版本

在这里插入图片描述

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

`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里面渲染更新进度样式

<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>

效果图(没有了,不是这个效果了)
在这里插入图片描述

在这里插入图片描述

新的效果:

在这里插入图片描述

自定义安装路径

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

新建vue.config.js

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。

在这里插入图片描述

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

修改 项目打包安装图标

1、安装依赖 生成图标

yarn add electron-icon-builder

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

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

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

在这里插入图片描述

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

yarn electron:build

5、图标已更改

在这里插入图片描述

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

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

'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()
		})
	}
}

项目总体目录结构

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

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嗬呜阿花

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值