render层与main层的通信
使用ipcRenderer进行通信,为electron自带对象。使用方式类似于事件通信
检查更新
思路
- 安装依赖
- 编写renderer层和main层的检查更新逻辑,autoupdater提供了热更新的方法
- 设置package和build.json等
1. 安装依赖
注意,需要安装在生产环境(dependencies)中。因为是在应用使用中会使用的功能
npm install electron-updater
2. 编写逻辑
思路:
- 编写renderer层和main层的通信,当页面点击检查更新时通信给main层进行检查。检查过程中出现进度信息再通信给renderer层。
- 编写检查更新的逻辑。同时还有退出更新的逻辑
- 进行测试
-
编写通信方法
- renderer层使用electron的ipcRenderer对象进行监听和发送
src/renderer/utils/ipcRenderer.ts
//* 用于向main层通信的封装方法 const { ipcRenderer } = require("electron"); //* vite引入electron需要使用require export function send(code: string) { //* 发送事件 ipcRenderer.invoke(code) } export function on(code: string, callback: (event, age: unknown) => void) { //* 监听事件 return ipcRenderer.on(code, callback) } export default { send, on }
- main层使用electron的ipcMain进行监听,使用mainWindow的webContents进行发送
src/main/services/ipcMain.ts 用于监听事件
//* 用于管理main层的监听工具类
import { ipcMain } from "electron"
import AppUpdate from './checkUpdate'
//* 引入工具类
import { getMainWindowByIpcEvent } from '../utils/MainWindow'
let appUpdate = null
function onEvent() { //* 监听从rendener层传送过来的数据
ipcMain.handle('check-update', (event) => { //* 监听检查更新事件
appUpdate.checkUpdate(getMainWindowByIpcEvent(event))
})
ipcMain.handle('confirm-update', () => {
appUpdate.quitAndInstall()
})
}
export function Mainfunc() {
//* 初始化对象 用于检查更新 下方会使用到
appUpdate = new AppUpdate()
//* 监听事件
onEvent()
}
//* 用于管理ipc通信
export default {
}
src/main/utils/MainWindow.ts 工具类,提供获取mainwindow的方法和发送信息的方法
//* 关于mainWindow的工具类
import { BrowserWindow, IpcMainInvokeEvent } from "electron";
//* 根据Ipc获取mainWindow
export function getMainWindowByIpcEvent(event: IpcMainInvokeEvent) {
return BrowserWindow.fromWebContents(event.sender);
}
//* 发送信息给渲染层(vue)
export function sendToRender(
mainWindow: BrowserWindow | undefined | null,
code: string,
type: number,
data?: string
) {
if(!mainWindow) {
return
}
const senddata = {
state: type,
msg: data || "",
};
mainWindow.webContents.send(code, senddata);
}
2. 编写逻辑
- renderer层在点击检查更新时向main层发送通信。并且监听main层发送来的消息。比如无更新,更新下载进度等。以便做出渲染
src/renderer/pages/index/index.vue
<template>
<h1>更新界面111</h1>
<div class="actions">
<el-button @click="checkUpdate">
检查更新
</el-button>
</div>
<el-dialog
v-model="dialogVisible"
title="进度"
center
width="14%"
top="45vh"
>
<div class="conten">
<el-progress
type="dashboard"
:percentage="percentage"
:color="colors"
:status="progressStaus"
/>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { send, on } from '@renderer/utils/ipcRenderer'
import { messageByText, successMessage } from '@renderer/utils/El'
let percentage = ref(0);
let colors = ref([
{ color: "#f56c6c", percentage: 20 },
{ color: "#e6a23c", percentage: 40 },
{ color: "#6f7ad3", percentage: 60 },
{ color: "#1989fa", percentage: 80 },
{ color: "#5cb87a", percentage: 100 },
]);
let dialogVisible = ref(false);
let progressStaus = ref('');
function checkUpdate() {
//* 检查更新方法 发送通信
send('check-update') //通信到main层
}
//* 监听main层发送过来的数据
on('UpdateMsg', (event, age): void => {
console.log(event, age)
switch (age.state) {
case -1:
console.log('发生错误')
break
case 0:
messageByText("正在检查更新");
break;
case 1:
successMessage("已检查到新版本,开始下载");
dialogVisible.value = true;
break;
case 2:
successMessage("无新版本");
break;
case 3:
percentage.value = age.msg.percent.toFixed(1);
break;
case 4:
console.log('添加setTimeout事件')
setTimeout(() => {
messageByText('退出更新')
//发送退出并更新的通信 可写自己逻辑,比如询问是否立即更新等
send('confirm-update')
}, 5000);
progressStaus.value = "success";
successMessage('下载完成')
break;
default:
break;
}
})
</script>
- main层方法
src/main/services/checkUpdate.ts
//* 检查更新工具类
import { autoUpdater } from "electron-updater";
import { BrowserWindow } from 'electron';
//* 引入工具类
import { sendToRender } from '../utils/MainWindow';
import path from "path";
class CheckUpdate {
public mainWindow: BrowserWindow | undefined | null
constructor() {
//* 设置检查更新的url 可以不设置 忽略
// autoUpdater.setFeedURL("http://127.0.0.1:5500/");
//* 修改配置地址 dev使用 开发环境需要注释 根据自己路径来,需要获取app-update.yml
autoUpdater.updateConfigPath = path.join(__dirname, '../../../build/win-unpacked/resources/app-update.yml')
this.updaterEvent()
}
checkUpdate(mainWindow: BrowserWindow | null) { //* 开始检查更新
//* 赋值当前需要检查更新的窗口
this.mainWindow = mainWindow
autoUpdater.checkForUpdates().catch(err => {
console.log('网络连接问题', err)
})
}
// 退出并安装
quitAndInstall() {
autoUpdater.quitAndInstall()
}
updaterEvent() { //* 监听updater的事件
/**
* -1 检查更新失败 0 正在检查更新 1 检测到新版本,准备下载 2 未检测到新版本 3 下载中 4 下载完成
**/
// 当开始检查更新的时候触发
autoUpdater.on("checking-for-update", (event, arg) => {
console.log("开始检查更新", event, arg);
sendToRender(this.mainWindow,'UpdateMsg', 0)
});
// 发现可更新数据时
autoUpdater.on("update-available", (event, arg) => {
console.log("有更新", event, arg);
sendToRender(this.mainWindow,'UpdateMsg', 1)
});
// 没有可更新数据时
autoUpdater.on("update-not-available", (event, arg) => {
console.log("没有更新", event, arg);
sendToRender(this.mainWindow, 'UpdateMsg', 2)
});
// 下载监听
autoUpdater.on("download-progress", (progressObj) => {
console.log(progressObj, '下载监听')
sendToRender(this.mainWindow,'UpdateMsg', 3, progressObj)
});
// 下载完成
autoUpdater.on("update-downloaded", () => {
console.log("下载完成");
sendToRender(this.mainWindow,'UpdateMsg', 4)
});
// 当更新发生错误的时候触发。
autoUpdater.on('error', (err) => {
console.log('更新出现错误', err.message)
if (err.message.includes('sha512 checksum mismatch')) {
sendToRender(this.mainWindow, -1, 'sha512校验失败')
} else {
sendToRender(this.mainWindow, -1, '错误信息请看主进程控制台')
}
})
}
}
export default CheckUpdate
从这里可以再去看看上面通信层,会调用到checkUpdate的方法
build.json:修改build.json的同时package.json的启动命令也需要修改,详情可以看我写的入门文章
{
"publish": [// autoUpdate检查更新的配置
{
"provider": "generic",
"url": "http://127.0.0.1:5500/" // 为资源地址的路径
}
],
}
3. 进行测试
- 可以使用vscode的Live Server插件创建一个本地文件服务器。
- 修改package.json的version属性(比当前版本高)并进行打包
- 将打包后的build文件夹下面的文件全部复制到资源文件夹路径下面(autoUpdate会检查latest.yml比对版本,如果需要更新下载则下载exe文件)
坑
- vite引入require 需要引入依赖,并且在tsconfig.json中配置
- dev环境下报不能检查的错误
-
引入依赖为@types/node
配置tsconfig.json
"types": [ "vite/client"], //** 设置vite兼容(vite-client)用于使用require -
dev环境下报不能检查的错误
src/main/index.ts
import { app } from 'electron' // 生产环境下需要注释 Object.defineProperty(app, 'isPackaged', { get() { return true; } });
api
- .checkForUpdates() 执行一次检查更新
- .checkForUpdatesAndNotify() 执行一次检查更新,如果有新的可用更新,还会自定弹出一个自带的通知提示告诉用户有新的更新
- .downloadUpdate(cancellationToken) 执行下载安装包
- .quitAndInstall(isSilent, isForceRunAfter) 退出应用并安装更新, isSilent 是否静默更新,isForceRunAfter更新完后是否立即运行
事件
- error 检查更新错误
- checking-for-update 正在检查更新
- update-available 有新的可用更新
- update-not-available 没有可用的更新,也就是当前是最新版本
- download-progress 正在下载更新版,会有更新进度对象
- update-downloaded 新安装包下载完成
配置项
- autoDownload = true 有可用更新时是否自动下载
- autoInstallOnAppQuit = true 如果安装包下载好了,那么当应用退出后是否自动安装更新
- allowPrerelease = false 是否接受开发版,测试版之类的版本号
- allowDowngrade = false 是否可以回退版本,比如从开发版降到旧的稳定版