electron-updater实现electron全量更新和增量更新——主进程部分

同学们可以私信我加入学习群!


正文开始

  • 前言
  • 更新功能所有文章汇总
  • 一、更新插件选择
  • 二、在main.js中引入我们的更新模块
  • 三、更新模块UpdateController.js暴露的方法checkUpdate
  • 四、更新模块UpdateController.js中的监听
    • 4.1监听是否有新版本需要更新?
    • 4.2 监听更新时的下载信息
    • 4.3 监听下载完成
  • 五、完整的更新逻辑
  • 六、优化后的监听
  • 6.1新增检查更新的监听
    • 6.2 新增执行更新的监听
    • 6.3 新增安装的监听
    • 6.4优化第四节的几个更新
  • 总结
  • 附件


前言

最近好久不更文,一是公司的事确实很忙,二是把时间都用在开发工具上了,写文总是提不起兴趣。

好消息是工具总算是憋出来几个,坏消息功能怎么实现的,代码快忘差不多了。我经常看着我写过的代码一脸茫然:这真的是我写的???

软件目前功能汇总:svga预览、node版本管理、前端部署-nginx管理、webstorm破解、浏览器插件等。
在这里插入图片描述
围绕这些功能,可能会重新开一个文章系列——pc工具源码系列,详细讲解它们都是怎么实现的。但是对它们的讲解放在这里不太合适,因为本系列主要讲解的是electron的基础技能,demo只是辅助。

唠了这么多,其实本文重点要讲解的内容是——electron更新。

更新功能所有文章汇总

  1. electron-updater实现electron全量更新和增量更新——主进程部分
  2. electron-updater实现electron全量更新和增量更新——注意事项/技巧汇总
  3. electron-updater实现electron全量更新和增量更新——渲染进程UI部分
  4. electron-updater实现electron全量更新和增量更新——渲染进程交互部分

一、更新插件选择

官网给了简单的更新api:autoUpdater,可以自行查看。
在这里插入图片描述
本文采取的是更新插件:electron-updater。

选择它的理由也很简单,因为它是electron-builder打包工具推荐的更新插件,看过前面文章的同学应该知道,我的项目打包工具都是基于electron-builder。

所以选择electron-updater不仅可以和electron-builder更契合,也会有便捷的增量更新功能。简单好用,就是理由。

二、在main.js中引入我们的更新模块

能接触到更新这一步的同学,手里的项目肯定是已经存在main.js等文件。

如果把所有的逻辑都放到main.js中,最终main.js会过于臃肿。为了更好地组织代码,我们需要把更新部分的功能放到一个独立的文件中:UpdateController.js

在main.js中引入这个更新功能模块,代码可能如下:

//引入更新功能
const checkUpdate = require('./controller/UpdateController');app.whenReady().then(() => {let win = new getWindow().createWindow() //创建窗口const ipcSend=require('./ipc/ipc-send')ipcSend.init(win)  //监听渲染进程checkUpdate(win);  //检查更新new getMenuPersonal(win).createMenu()  //创建工具栏// 注册快捷键监听器getGlobalShortcut.create(win)
})

上面和更新相关的代码主要是两行:

const checkUpdate = require('./controller/UpdateController');
checkUpdate(win);  //检查更新

checkUpdate 是UpdateController中暴露的方法,接收一个window对象,这个window对象用于主进程向渲染进程主动通信时使用:mainWin.webContents.send,后文会涉及。

三、更新模块UpdateController.js暴露的方法checkUpdate

首先通过npm下载electron-updater:

npm i electron-updater

然后再更新模块中引用electron-updater

const { autoUpdater } = require('electron-updater');

上一节把更新模块放到main.js中时,我们提到过更新模块暴露的方法checkUpdate,下面我们来看一下它的具体实现:

let mainWin = null;
const checkUpdate = (win) => {mainWin = win;if(app.isPackaged){autoUpdater.setFeedURL('http://xxxxx:8888/updater/')}else{autoUpdater.setFeedURL('http://localhost:8888/updater/')}autoUpdater.forceDevUpdateConfig = true //开发环境下强制更新autoUpdater.autoDownload = false; // 自动下载autoUpdater.autoInstallOnAppQuit = true; // 应用退出后自动安装
};

可以看到我的checkUpdate实现十分简单,它主要的作用就是操作autoUpdater对象完成一些基础配置。

  • 我们在方法体外定义了一个mainWin全局变量,checkUpdate方法中,首先为mainWin变量赋值从main.js中传来的window对象。
  • autoUpdater.setFeedURL是设置更新的远程地址,设置的地址下应当能直接看到我们的exe文件,electron-updater插件会自动从这个远程地址下,获取最新安装包。
  • 下面是autoUpdater的一些配置,重点注意autoDownload要设置为false,不要在检测到更新时,自动下载,对用户体验不好。我们应该能让用户控制下载、跳过下载等操作。

我不喜欢把和下载相关的所有操作一股脑都放到checkUpdate方法中,尤其是一些监听。checkUpdate的职责应该单一而纯粹,只是在做一些autoUpdater对象的基础配置。

如果你不喜欢分层分类地去构建代码,那把和更新相关的监听都放到这个方法,也是可行的。

四、更新模块UpdateController.js中的监听

更新模块的职责其实可以很简单,它可以分为两个部分,就完成最基础的更新功能:

  1. 一是对autoUpdater对象的配置,让更新插件知道以什么效果去执行更新。这部分工作上面已经做了。
  2. 二是监听更新全生命周期,让electron知道更新进行到哪一步了,都需要做什么操作。

这就是最简单的一个更新功能。这节内容,就是要监听更新的全生命周期。

4.1监听是否有新版本需要更新?

autoUpdater.on('update-available', (info) => {console.log('有新版本需要更新',info);//这里可以写个主进程到渲染进程的通信,主动告诉渲染进程;//因为我实际项目中的逻辑要略复杂,所以这里先省略
});
autoUpdater.on('update-not-available', (info) => {console.log('无需更新');//业务代码
});

4.2 监听更新时的下载信息

autoUpdater.on('download-progress', (prog) => {let speed=prog.bytesPerSecond / 1000000>1?Math.ceil(prog.bytesPerSecond / 1000000)+'M/s':Math.ceil(prog.bytesPerSecond / 1000)+'K/s'mainWin.webContents.send('pc-update-progress',  {speed, // 网速percent: Math.ceil(prog.percent), // 百分比});});

prog参数里,有更新过程中所有的信息,我们可以根据里面信息来计算我们需要的参数。网速就是个估算值,较真你就输了,但是百分比必须要准,不能学某些软件,前面百分之99用时1秒钟,最后百分之1用时1小时。

4.3 监听下载完成

autoUpdater.on('update-downloaded', (info) => {isDownloading=falsemainWin.webContents.send('pc-downloaded');  //告诉页面,更新完成了// 下载完成后强制用户安装,不推荐// autoUpdater.quitAndInstall();
});

监听的最简代码至此就完成了。

五、完整的更新逻辑

如果按照上面的代码照搬,大概率是不会触发更新的,因为里面还缺少了一个关键的触发方法:autoUpdater.checkForUpdatesAndNotify()。这个方法是检查更新的api,只有调用它,才会触发后续一系列监听。

大部分文章都把这个方法放到checkUpdate方法中,意味着当electron主进程加载时,就会调用checkUpdate方法,此时就会同步检查更新,并触发对应的监听方法。

可这样合理吗?会不会更新逻辑运行完,向渲染进程通信了消息,但是渲染进程还未结束,导致显示出现异常?可能会有人在checkUpdate方法执行的地方增加setTimeout,以确保更新的逻辑都正常运行。但是当项目变大,逻辑变复杂后,写setTimeout强行异步的方式,就是混乱之源。

我们正常的更新逻辑,不应该是主进程加载后,就检查更新,而应该是页面加载后,检查更新,并获取更新模块反馈的信息。

因为你不知道是主进程更新逻辑运行得快,还是页面渲染得快。即使在某些电脑上,主进程更新逻辑运行速度优于页面渲染速度,最终表现正常,也无法保证在不同性能电脑上都能表现一致。

所以现在的完整逻辑就是:

第一步:页面渲染完毕,并询问主进程,是否有更新?
第二步:主进程检查更新,并反馈给页面,有更新/无更新。
第三步:如果无更新,页面直接显示提示信息。如果有更新,页面产生交互逻辑,将决定权交给用户,用户决定是否更新。
第四步:用户点击更新,页面将指令发送给主进程,主进程开始执行更新。

六、优化后的监听

6.1新增检查更新的监听

经过优化后,我们需要设计有用户交互逻辑的更新功能,第一步就是要监听页面渲染完毕后,询问主进程是否有更新,并把结果反馈给页面,这是一个双向通信。

很多监听都会给页面反馈消息,所以我们创建一个反馈信息的全局变量:judgeRs。

用户可能会刷新页面,这时页面会重新渲染,重新发送检查更新的信息,如果不加控制,就会出现重复的更新下载,所以我们创建一个控制是否检查更新的全局变量:isDownloading。

let judgeRs={}
let isDownloading=falseipcMain.handle('check-pc-update',async ()=>{try {if(isDownloading){return {success:true,isDownloading:true,msg:'正在下载中,请稍后'}}else{const res= await autoUpdater.checkForUpdatesAndNotify()console.log('judge',res)//如果check结果正常,则使用上面监听构造的judgeRsreturn judgeRs}}catch (e){//    check报错judgeRs = {success: false,msg: '没有更新包:博主财力有限,服务器被下架了,软件最新版本,请通过"中二少年工具箱"小程序,查询网盘下载地址'}return judgeRs}
})

6.2 新增执行更新的监听

通过上面优化后的更新逻辑,我们知道,更新操作不再是自动进行,而是由用户点击按钮操作的。所以要监听用户的操作,并触发更新。这是由渲染进程到主进程的单向通信。

/*监听渲染进程指令,执行更新*/
ipcMain.on('send-update', () => {autoUpdater.autoDownload = true;autoUpdater.checkForUpdates();
})

注意autoUpdater.autoDownload = true;这就是在checkUpdate方法中,为什么要把autoDownload默认设置成false,因为如果默认是true,就无法实现由用户控制更新。在’check-pc-update’监听中,执行检查更新autoUpdater.checkForUpdatesAndNotify()方法时,就会自动更新下载安装包。我们就是通过autoDownload 属性的开闭,来实现是否下载的控制。

6.3 新增安装的监听

更新下载完毕后,是否立即安装,也应该由用户控制。

// 监听渲染进程的 install 事件,触发退出应用并安装
ipcMain.handle('pc-install', () => autoUpdater.quitAndInstall());

6.4优化第四节的几个更新

在本节中,增加了两个全局变量judgeRs、isDownloading。

judgeRs在是否有更新的监听中,可以赋值,如下:

autoUpdater.on('update-available', (info) => {console.log('有新版本需要更新',info);judgeRs={success:true,needUpdate:true,msg:'有新版本需要更新',version:info.version}
});
autoUpdater.on('update-not-available', (info) => {console.log('无需更新');judgeRs={success:true,needUpdate:false,msg:'无需更新'}
});

当监听到正在下载资源时,可以把isDownloading赋值为true:

autoUpdater.on('download-progress', (prog) => {let speed=prog.bytesPerSecond / 1000000>1?Math.ceil(prog.bytesPerSecond / 1000000)+'M/s':Math.ceil(prog.bytesPerSecond / 1000)+'K/s'mainWin.webContents.send('pc-update-progress',  {speed, // 网速percent: Math.ceil(prog.percent), // 百分比});isDownloading=true});

至此,主进程所有的更新操作就完成了。后续还可以增加强制更新、回退版本等各种功能。


总结

大家如果需要联系博主,或者获取博主各系列文章对应的资源,可以通过私信博主来获取。

有任何前端项目、demo、教程需求,都可以联系博主,博主会视精力更新,免费的羊毛,不薅白不薅!~

附件

更新模块UpdateController完整的代码参考:

const { autoUpdater } = require('electron-updater');
const {ipcMain,app} = require('electron')let mainWin = null;
let judgeRs={}
let isDownloading=false
const checkUpdate = (win) => {mainWin = win;if(app.isPackaged){autoUpdater.setFeedURL('http://lizetoolbox.top:83/updater/lize-tools-pc')}else{autoUpdater.setFeedURL('http://localhost:83/updater/lize-tools-pc/')}autoUpdater.forceDevUpdateConfig = true //开发环境下强制更新autoUpdater.autoDownload = false; // 自动下载autoUpdater.autoInstallOnAppQuit = true; // 应用退出后自动安装
};autoUpdater.on('update-available', (info) => {console.log('有新版本需要更新',info);judgeRs={success:true,needUpdate:true,msg:'有新版本需要更新',version:info.version}
});
autoUpdater.on('update-not-available', (info) => {console.log('无需更新');judgeRs={success:true,needUpdate:false,msg:'无需更新'}
});// 监听渲染进程的 install 事件,触发退出应用并安装
ipcMain.handle('pc-install', () => autoUpdater.quitAndInstall());ipcMain.handle('check-pc-update',async ()=>{try {if(isDownloading){return {success:true,isDownloading:true,msg:'正在下载中,请稍后'}}else{const res= await autoUpdater.checkForUpdatesAndNotify()console.log('judge',res)//如果check结果正常,则使用上面监听构造的judgeRsreturn judgeRs}}catch (e){//    check报错judgeRs = {success: false,msg: '没有更新包:博主财力有限,服务器被下架了,软件最新版本,请通过"中二少年工具箱"小程序,查询网盘下载地址'}return judgeRs}
})
autoUpdater.on('download-progress', (prog) => {let speed=prog.bytesPerSecond / 1000000>1?Math.ceil(prog.bytesPerSecond / 1000000)+'M/s':Math.ceil(prog.bytesPerSecond / 1000)+'K/s'mainWin.webContents.send('pc-update-progress',  {speed, // 网速percent: Math.ceil(prog.percent), // 百分比});isDownloading=true});
autoUpdater.on('update-downloaded', (info) => {isDownloading=falsemainWin.webContents.send('pc-downloaded');// 下载完成后强制用户安装,不推荐// autoUpdater.quitAndInstall();
});
/*监听渲染进程指令,执行更新*/
ipcMain.on('send-update', () => {autoUpdater.autoDownload = true;autoUpdater.checkForUpdates();
})
module.exports = checkUpdate;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/393765.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

怎样配置虚拟机IP

目录(三步走) 配置本机IP 配置虚拟机外部IP 配置虚拟机内部IP 参考链接: 配置本机IP 打开“网络和共享中心”——>更改“适配器设置” 找到“VMnet8”,然后右键“属性”,弹出下列窗口 输入本机IP(你…

浅谈操作系统

我们前面谈到了一个可执行程序首先会到内存进行预先加载~而在我们的计算机中第一个被加载的软件就是操作系统~ 操作系统的主要工作就是对软硬件资源进行管理~ 这里我们先从操作系统下层开始讲起~ 我们把操作系统类比为校长,驱动程序类比为辅导员,底层硬件…

【学术会议征稿】第四届电气工程与计算机技术国际学术会议(ICEECT2024)

第四届电气工程与计算机技术国际学术会议(ICEECT2024) 2024 4th International Conference on Electrical Engineering and Computer Technology 第四届电气工程与计算机技术国际学术会议(ICEECT2024)将于9月27日-29日在哈尔滨举…

吴恩达机器学习COURSE1 WEEK2

COURSE1 WEEK2 多维特征 在线性回归中,往往特征不止一个,而是具有多维特征 例如,在预测房价的例子中,我们知道更多的信息: x 1 x_1 x1​:房屋的面积 x 2 x_2 x2​:卧室的数目 x 3 x_3 x3​&a…

微信小程序 - 自定义计数器 - 优化(键盘输入校验)

微信小程序通过自定义组件,实现计数器值的增加、减少、清零、最大最小值限定、禁用等操作。通过按钮事件触发方式,更新计数器的值,并修改相关联的其它变量。通过提升用户体验,对计数器进行优化设计,使用户操作更加便捷…

蜂窝网络架构

2G/3G 4G eNB RF-RRU eCPRI RRU-BBU 光纤 5G From 38.300 AMF处理信令等,UPF 用户面,后面还有SMF

医院不良事件监测预警上报系统,PHP不良事件管理系统源码

不良事件上报系统,支持医院进行10大类医疗安全(不良)事件的上报管理;帮助医院管理部门更好把控不良事件的发生趋势,分析医院内部潜在的问题和风险,采取适当的管理措施,有效加强质量控制&#xf…

MySQL总体功能

基于Innodb存储引擎的讨论 MySQL 核心功能 功能解决的问题ACID模型数据并发访问,和奔溃恢复安全问题,一致性&奔溃恢复索引数据查询效率问题备份容错设计,解决硬件错误带来的问题复制数据迁移监控执行数据库操作的异常记录

JavaEE: wait(等待) / notify (通知)

文章目录 wait(等待) / notify (通知)总结 wait(等待) / notify (通知) 线程在操作系统上的调度是随机的~ 那么我们想要控制线程之间执行某个逻辑的先后顺序,那该咋办呢? 可以让后执行的逻辑,使用wait, 先执行的线程,在完成某些逻辑之后,通过notify来唤醒对应的wait. 另外,通…

C++-类与对象基础

一,类的定义 1.1类定义格式 class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。类体中内容称为mian类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者…

【nginx】centos7安装并配置开机自启

【nginx】配置开机自启 1.nginx配置开机自启 安装完成nginx之后 vim /lib/systemd/system/nginx.service[Unit] Descriptionnginx Afternetwork.target[Service] Typeforking ExecStart/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf ExecReload/usr/loc…

理解Spring框架2:容器IOC

理解Spring框架2:容器IOC (qq.com)

ViP-LLaVA: Making Large Multimodal Models Understand Arbitrary Visual Prompts

发表时间:cvpr2024 论文链接:https://readpaper.com/pdf-annotate/note?pdfId2357936887983293952&noteId2426262228488986112 作者单位:University of Wisconsin–Madison Motivation:现在的多模态模型都关注整张图像的理…

torch量化接口深度解读-eager模式-fx模式

一、定义 接口总结量化模式解读 二、实现 接口总结 1. PyTorch提供了三种不同的量化模式:Eager模式量化、FX图模式量化(维护)和PyTorch 2导出量化。 2. Eager Mode Quantization是一个测试版功能。用户需要进行融合,并手动指定量…

尚硅谷谷粒商城项目笔记——六、使用navciat连接docker中的mysql容器【电脑CPU:AMD】

六、使用navciat连接docker中的mysql容器 注意: 因为电脑是AMD芯片,自己知识储备不够,无法保证和课程中用到的环境一样,所以环境都是自己根据适应硬件软件环境重新配置的,这里的虚拟机使用的是VMware。 1navicat免费…

最新版Ableton Live 12.20 WIN MAC,长期更新持续有效

一。Ableton Live 12.20 WIN &MAC 2024.08.06发布 Ableton Live Suite是一款由ABLETON公司开发的功能强大且全面的音乐制作、内容编辑和演奏分析软件。它极大地改进了许多社会功能,使音乐创作、背景音乐的开发变得更加快捷方便。 软件的主要功能包括录音、作曲…

WordPress原创插件:Category-id-list分类ID显示查看

WordPress原创插件:Category-id-list分类ID显示查看 插件设置位置在工具栏

学习vue3 五,传送,缓存组件以及过渡和过渡列表

目录 Teleport传送组件 keep-alive缓存组件 transition动画组件 1. 过渡的类名 2. 自定义过渡class名 3. transition的生命周期 4.appear transition-group 1. 过渡列表 2. 列表的移动过渡 3. 状态过渡 Teleport传送组件 Teleport Vue 3.0新特性之一。 Teleport 是一…

Spring的配置类分为Full和Lite两种模式

Spring的配置类分为Full和Lite两种模式 首先查看 Configuration 注解的源码, 如下所示: Target({ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) Documented Component public interface Configuration {AliasFor(annotation Component.class)String value() defau…

(C23/C++23) 语句末尾的标签

文章目录 🔖前言🏷️ref🏷️标号 🔖兼容🏷️23标准前🏷️23标准后🏷️原因 🔖未兼容🔖END🌟关注我 🔖前言 🏷️ref C23提案复合语句末…