electron自动更新(electron-updater)windows系统

代码环境 vue3+vite+electron

主要工具版本

  • "electron-updater": "^6.1.1"
  • "electron": "^15.1.2",
  • "electron-builder": "^23.6.0",

逻辑解析

使用electron-builder打包配置生成exe安装包(winodow),将最新的exe包放在线上地址。本地用户使用低版本的时候会出提示发现最新版本,从而下载远端线上的exe包。从而进行安装。

放在远端的文件

哪些不用我不太清楚

需要注意!我们线上的链接要做到。直接访问exe的路径是下载。访问yml的路径是在网页上打开。具体咋办,我不知道咋弄。

基础代码。

使用electron的弹出框进行升级

json 复制代码
package.json文件
{
    "version": "1.6.0",用于判断是否需要判断的字段
    ......
    "build": {
    .......
        "publish": [用于后构建时候生成latest.yml等附属文件
            {
                "provider": "generic",固定字段
                "url": "http://127.0.0.1:8080"
                这个玩意随便填写,因为我们用的electron-builder更新
            }
        ],
    ......
    },
    "directories": {这个vite时候有用
        "buildResources": "build",
        "output": "dist_electron"
    }
}
js 复制代码
生成electron窗口的js文件中

const { app, BrowserWindow, dialog } = require('electron')
const { autoUpdater } = require("electron-updater");
const feedUrl = `放在远端的链接/${process.platform}`;
//process.platform其实就是`win32`字段因为我们做的是固定window64升级的嘛
//所以也可以写成const feedUrl = `放在远端的链接`;

let webContents;

let sendUpdateMessage = (message, data) => {
    webContents.send("message", { message, data });
};
let checkForUpdates = () => {
    autoUpdater.setFeedURL(feedUrl);
    autoUpdater.on(
        "update-downloaded",
        function (
            event,
            releaseNotes,
            releaseName,
            releaseDate,
            updateUrl,
            quitAndUpdate
        ) {
            dialog
                .showMessageBox({
                    type: "info",
                    title: "应用更新",
                    message: "发现新版本,是否更新?",
                    buttons: ["是", "否"],
                })
                .then((buttonIndex) => {
                    if (buttonIndex.response == 0) {
                        autoUpdater.quitAndInstall();
                    }
                });
        }
    );

    //执行自动更新检查
    autoUpdater.checkForUpdates();
};
app.on('ready', () => {
    let win = createWindow()
    webContents = win.webContents;
    // 一次更新检车
    checkForUpdates();
})

使用自定义(html)弹出框进行升级,并且可以自定义说明该版本更新的内容

package.json文件内容相同

electron-builder生成后的latest.yml文件

info字段为手动添加后在上传到远端服务器上。只有info为自定义的,其余都是生成的。且可以多个自定义字段,在autoUpdater.on"update-available""update-not-available"后的参数info都可以获取到。

js 复制代码
生成electron窗口的js文件中

const { app, BrowserWindow } = require('electron')
const { autoUpdater } = require("electron-updater");
const feedUrl = `放在远端的链接/${process.platform}`;
//process.platform其实就是`win32`字段因为我们做的是固定window64升级的嘛
//所以也可以写成const feedUrl = `放在远端的链接`;

let webContents;

let sendUpdateMessage = (message, data) => {
    webContents.send("message", { message, data });
};
//我改名字了,因为两个名字一样我看的不舒服
let updateHandle = () => {
    autoUpdater.setFeedURL(feedUrl);
    autoUpdater.autoDownload = false

    autoUpdater.on("error", (data) => {
        sendUpdateMessage("更新报错", data);
    });
    autoUpdater.on("checking-for-update", (data) => {
        sendUpdateMessage("判断是否需要更新");
    });
    // 触发更新时候
    autoUpdater.on("update-available", (info) => {
        //info可以读取latest.yml中的内容
        sendUpdateMessage("不是最新版本需要更新", info);
    });
    autoUpdater.on("update-not-available", function (ifo) {
        sendUpdateMessage("最新版本不用更新", ifo);
    });
    autoUpdater.on('download-progress', (progressObj) => {
        sendUpdateMessage('正在下载,获取进度条', progressObj);
        /*后续有用,官方数据展示(下图
        progressObj:typeof{
           progress
           bytesPerSecond
           percent
           total
           transferred
        }
        */
    })
    autoUpdater.on(
        "update-downloaded",
        function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
            sendUpdateMessage('下载结束')
            //会出现没有进度条进行的情况。
            //该情况是已经下载到本地了,但是你没有安装。
            sendUpdateMessage('正在下载,获取进度条', {
                percent:100
            });
        }
    )
    ipcMain.on("updateNow", () => {
        autoUpdater.quitAndInstall();
    });
    ipcMain.on("downloadUpdate", () => {
        autoUpdater.downloadUpdate()
    });
    ipcMain.on("checkForUpdate", () => {
        autoUpdater.checkForUpdates()
    })
};
app.on('ready', () => {
    let win = createWindow()
    webContents = win.webContents;
    // 一次更新检车
    updateHandle();
})
html 复制代码
App.vue页面vue3
<style lang="scss">
.dialog {
	padding: 0;
	border: none;
	background: transparent;
	.content {
		padding: 128px 76px 0 76px;
		box-sizing: border-box;
		width: 860px;
		height: 653px;
		background: url("./assets/update.png");
		background-size: cover;
		position: relative;
	}
	.main {
		height: 300px;
		width: 100%;
		display: flex;
		flex-direction: column;
		justify-content: center;
		align-items: flex-start;
		color: #999999;
		padding: 30px 0;
		box-sizing: border-box;
		.bt {
			color: #333333;
			font-size: 24px;
			line-height: 36px;
			margin-bottom: 20px;
			flex-shrink: 0;
		}
		.info {
			overflow-y: auto;
			&::-webkit-scrollbar {
				width: 0;
			}
		}
	}
	.close-btn {
		width: 46px;
		object-fit: contain;
		position: absolute;
		top: 42px;
		right: 14px;
		cursor: pointer;
	}
	.title-1 {
		font-size: 40px;
		font-weight: 700;
		line-height: 60px;
		letter-spacing: 2px;
		color: #ffffff;
		text-align: center;
	}
	.title-2 {
		font-size: 24px;
		line-height: 36px;
		letter-spacing: 1.2px;
		color: #ffffff;
		text-align: center;
	}
	&::backdrop {
		background: #00000080;
	}
	.operate-box {
		text-align: center;
		button {
			cursor: pointer;
			width: 160px;
			height: 50px;
			border-radius: 5px;
			background: #377dff;
			text-align: center;
			border: none;
			outline: none;
			line-height: 50px;
			color: #ffffff;
			&:first-of-type {
				margin-right: 80px;
			}
		}
	}
	.progress-bar {
		width: 100%;
		height: 20px;
		background-color: #e4e8fa;
		border-radius: 20px;
		// overflow: hidden;
		position: relative;
	}

	.progress-bar::before {
		content: "";
		display: block;
		height: 100%;
		background: linear-gradient(
			-45deg,
			#5a7ace 10%,
			#699de7 0,
			#699de7 50%,
			#5a7ace 0,
			#5a7ace 60%,
			#699de7 0
		);
		background-size: 20px 20px;
		// transition: width 1s ease-in-out;
		width: var(--lk-width);
		border-radius: 20px;
	}

	.progress-bar span.t {
		width: 60px;
		height: 34px;
		border-radius: 26px;
		border: 2px solid #fff;
		background: #2b459c;
		color: #fff;
		text-align: center;
		position: absolute;
		top: -8.5px;
		left: calc(var(--lk-width) - 30px);
		font-size: 22px;
		font-weight: 600;
		span {
			font-size: 14px;
			font-weight: 400;
		}
	}
}
</style>
<template>
	<dialog ref="dialog" class="dialog">
		<div class="content">
			<img
				class="close-btn"
				src="./assets/close.png"
				@click="dialog.close()"
			/>
			<template v-if="state.isError">
				<div class="title-1">升级失败</div>
				<div class="title-2">很抱歉,软件升级失败</div>
				<div class="main">
					<div class="bt">您可以联系工作人员</div>
					<div class="info">
						<p>姓名:XXX</p>
						<p>联系方式:XXXXXXXXXXX</p>
					</div>
				</div>
				<div class="operate-box">
					<template v-if="!state.isLoading">
						<button
							style="background: #bebebe"
							@click="dialog.close()"
						>
							下次升级
						</button>
						<button
							@click="
								state.isError = false;
								methods.updateExe();
							"
						>
							重新升级
						</button>
					</template>

					<div
						v-else
						class="progress-bar"
						:style="{ '--lk-width': state.progressValue + '%' }"
					>
						<span class="t"
							>{{ state.progressValue }}<span>%</span>
						</span>
					</div>
				</div>
			</template>
			<template v-else
				><div class="title-1">
					{{ state.isLoading ? "软件升级中" : "软件升级" }}
				</div>
				<div class="title-2">
					{{
						state.isLoading
							? "正在升级软件,请勿进行任何操作!"
							: "快来体验新版本吧!"
					}}
				</div>
				<div class="main">
					<div class="bt">更新内容:</div>
					<div class="info" v-html="state.updateInfo"></div>
				</div>
				<div class="operate-box">
					<template v-if="!state.isLoading">
						<button
							style="background: #bebebe"
							@click="dialog.close()"
						>
							下次提醒
						</button>
						<button @click="methods.updateExe()">立即升级</button>
					</template>

					<div
						v-else
						class="progress-bar"
						:style="{ '--lk-width': state.progressValue + '%' }"
					>
						<span class="t"
							>{{ state.progressValue }}<span>%</span>
						</span>
					</div>
				</div></template
			>
		</div>
	</dialog>
	<router-view></router-view>
</template>
<script setup>
import { onMounted, reactive, ref } from "vue";

const dialog = ref(null);
const { ipcRenderer, ipcMain } = require("electron");

const state = reactive({
	isLoading: false,
	progressValue: 0,
	isError: false,
	updateInfo: "",
});
ipcRenderer.on("message", (event, { message, data }) => {
	console.group("--------------数据------------------");
	console.log(message);
	console.log(data);
	console.groupEnd();
	switch (message) {
		case "不是最新版本需要更新":
			dialog.value.showModal();
			state.updateInfo = data.info;
			break;
		case "正在下载,获取进度条":
			state.progressValue = Math.floor(data.percent);
			break;
		case "下载结束,可以安装":
			setTimeout((_) => {
				//为了让他看清楚进度条满了!
				ipcRenderer.send("updateNow");
			}, 1000);
			break;
		case "更新报错":
			state.isError = true;
			break;
		default:
			break;
	}
});

onMounted(() => {
	ipcRenderer.send("checkForUpdate");
});

const methods = {
	updateExe() {
		state.isLoading = true;
		ipcRenderer.send("downloadUpdate");
	},
};

需要注意(或者我遇到的坑

  1. 逻辑性,不可以在创建窗口函数中进行autoUpdater.checkForUpdates()需要在vue页面中通讯进行更新检测
  2. 本地测试,进度条可能会不走这个逻辑。因为本地已经下载完成,所以需要传递在下载文成时候弄个加进度条从而前端页面赋值让用户看到进度条;
  3. autoUpdater.downloadUpdate()需要和autoUpdater.autoDownload = false配合使用
总结!逻辑!
  1. autoUpdater.checkForUpdates()进行更新出发autoUpdater.on("checking-for-update")监听。
  2. 如果是最新版本不需要更新走autoUpdater.on("update-not-available")结束。
  3. 不是最新版本走autoUpdater.on("update-available")+autoUpdater.on('download-progress')+autoUpdater.on("update-downloaded")+autoUpdater.quitAndInstall();
  4. 如果设置了autoUpdater.autoDownload = false;需要触发autoUpdater.downloadUpdate()才能继续进行(一般都是去下载!)
相关推荐
樊南16 小时前
npm安装electron依赖时卡顿,下载不下来
前端·electron·npm
web前端进阶者16 小时前
electron-vite_15打包报错proxyconnect
前端·javascript·electron
407指导员16 小时前
electron 顶部的元素点不中,点击事件不生效
前端·javascript·electron
努力学前端Hang16 小时前
electron-vite打包后图标不生效问题
前端·javascript·electron
朝阳3916 小时前
electron-vite【实战】自定义标题栏【组件封装】(含异形标题栏,指定区域拖拽,窗口置顶,窗口最小化,窗口最大化,取消最大化,隐藏窗口到托盘等)
electron
朝阳3916 小时前
electron-vite【实战】登录/注册页
electron
他在时间门外16 小时前
使用Electron获取用户信息,监听程序打开,用户退出连接关闭程序【全代码,有图】
前端·javascript·electron
407指导员16 小时前
electron opacity 百分比设置不生效 变成1% 问题
前端·javascript·electron
森叶16 小时前
【附源码】Electron Windows桌面壁纸开发中的 CommonJS 和 ES Module 引入问题以及 Webpack 如何处理这种兼容
webpack·electron
正小安2 天前
Vite 系列课程|1课程道路,2什么是构建工具
前端·vite