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()才能继续进行(一般都是去下载!)
相关推荐
fishmemory7sec3 分钟前
Electron 主进程与渲染进程、预加载preload.js
前端·javascript·electron
YUELEI1185 分钟前
构建electron项目
electron·vue3
fishmemory7sec6 分钟前
Electron 使⽤ electron-builder 打包应用
前端·javascript·electron
Small-K1 天前
前端框架中@路径别名原理和配置
前端·webpack·typescript·前端框架·vite
姚*鸿的博客3 天前
electron出现乱码和使用cmd出现乱码
前端·javascript·electron
fishmemory7sec3 天前
Electron 使用 Nodemon 配置自动重启
前端·javascript·electron
itas1094 天前
Electron获取nodejs和chrome版本信息
javascript·chrome·electron·nodejs·node
码力巨能编4 天前
Electron应用创建和打包
javascript·arcgis·electron
fishmemory7sec4 天前
Electron 安装以及搭建一个工程
前端·javascript·electron
ZJ_.4 天前
VSCode调试Electron
javascript·ide·vscode·electron·node.js·编辑器