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

有任何问题,欢迎站内私信博主交流。



前言

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

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

软件目前功能汇总: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=false
    mainWin.webContents.send('pc-downloaded');  //告诉页面,更新完成了
    // 下载完成后强制用户安装,不推荐
    // autoUpdater.quitAndInstall();
});

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

五、完整的更新逻辑

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

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

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

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

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

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

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

六、优化后的监听

6.1新增检查更新的监听

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

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

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

let judgeRs={}
let isDownloading=false

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结果正常,则使用上面监听构造的judgeRs
            return 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

});

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

七、配置publish

electron-updater配合electron-builder实现更新,最终有三个关键文件必须要有:

  1. blockmap文件,记录着更新内容
  2. exe文件,新的二进制文件
  3. latest.yml,记录最新版本信息

要生成latest.yml文件,必须要在electron-builder的配置中设置publish字段:

"build": {
...省略其他配置
   "publish": [
      {
        "provider": "generic",
        "url": ""
      }
    ]
  }

url可以是远程服务器地址,也可以为空,但是不能没有这个字段。


总结

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

有任何前端项目、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结果正常,则使用上面监听构造的judgeRs
            return 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=false
    mainWin.webContents.send('pc-downloaded');
    // 下载完成后强制用户安装,不推荐
    // autoUpdater.quitAndInstall();
});
/*监听渲染进程指令,执行更新*/
ipcMain.on('send-update', () => {
    autoUpdater.autoDownload = true;
    autoUpdater.checkForUpdates();
})
module.exports = checkUpdate;

<think>好的,用户让我介绍ElectronVue结合使用时的全量更新增量更新。我需要先理清楚这两个概念在Electron应用中的具体应用方式。 首先,Electron应用的结构是分为主进程渲染进程的。主进程通常用Node.js编写,负责管理窗口原生操作,而渲染进程则用Vue这样的前端框架构建用户界面。更新机制主要涉及主进程如何下载、替换应用文件。 全量更新,应该是指每次更新都下载完整的安装包或应用文件,替换旧版本。这种方法简单可靠,但缺点是下载量大,尤其是应用体积大时,用户等待时间较长。需要确保下载的完整性,比如通过哈希校验。 增量更新则是只下载变化的部分,比如差异文件或补丁,然后在客户端合并。这种方法节省带宽,速度快,但实现复杂,需要处理合并的可靠性,特别是跨平台时的兼容性问题。另外,版本管理也是个挑战,必须确保用户当前的版本能够正确应用差异包。 接下来要考虑的是如何实现这两种更新Electron常用的更新方案有electron-updater,它支持全量更新。开发者需要配置自动更新服务器,比如使用Nuts或自己搭建的HTTPS服务器。对于增量更新,可能需要结合bsdiff或hdiff等工具生成差异包,然后客户端下载后合并。不过Electron本身没有内置支持,需要自己实现或使用第三方库。 用户可能的需求场景:如果他们希望快速部署且应用体积不大,全量更新可能更合适,维护简单。如果应用较大或用户网络条件差,增量更新能提升体验。但要注意增量更新的维护成本,比如生成测试补丁的工作量。 可能用户没有说出来的深层需求是希望平衡更新效率实现的复杂性。需要根据项目具体情况选择方案。另外,安全性回滚机制也是需要考虑的因素,特别是增量更新出错时如何处理。 总结回答结构:先分别介绍全量增量更新的定义、优缺点,再讲实现方式,最后比较选择建议。确保用户能理解两者的区别适用场景,帮助他们做出决策。</think>在 **Electron + Vue** 应用中,全量更新增量更新是两种常见的应用升级策略,用于确保用户使用的客户端始终是最新版本。以下是两者的详细介绍实现思路: --- ### **1. 全量更新** #### **定义** 全量更新是指每次更新时,客户端从服务器下载完整的应用程序安装包(如 `.exe`、`.dmg`、`.deb` 等),替换本地旧版本文件,实现版本升级。 #### **实现方式** 1. **Electron 内置方案** 使用 Electron 的 [electron-updater](https://www.electron.build/auto-update) 模块(集成于 `electron-builder`),配置自动更新服务: ```javascript const { autoUpdater } = require('electron-updater'); autoUpdater.checkForUpdatesAndNotify(); ``` - 需要将安装包托管在静态服务器(如 Amazon S3、GitHub Releases 或私有服务器)。 - 通过 `latest.yml`(或 `latest-mac.yml` 等)文件描述版本信息。 2. **自定义方案** - 通过接口获取最新版本号,比对本地版本。 - 若需要更新,下载完整安装包并触发静默安装(需调用系统命令)。 #### **优点** - 实现简单,可靠性高。 - 适合小型应用或更新频率低的场景。 #### **缺点** - 下载体积大,用户等待时间长。 - 流量消耗较高(尤其是大版本迭代)。 --- ### **2. 增量更新** #### **定义** 增量更新仅下载新旧版本之间的差异文件(如二进制差异补丁),在客户端本地合并差异,减少下载体积。 #### **实现方式** 1. **生成差异文件** - 使用工具(如 `bsdiff`、`hdiff` 或 `Courgette`)对比新旧版本,生成差异包(`.patch` 文件)。 - 示例命令: ```bash bsdiff old.exe new.exe patch.patch ``` 2. **客户端合并** - 客户端下载差异包后,使用对应工具合并旧版本文件,生成新版本。 - 需确保合并逻辑跨平台兼容(Windows/macOS/Linux)。 3. **版本管理** - 服务器需维护不同版本间的差异包映射关系(如 `v1.0.0 -> v1.0.1` 的补丁)。 #### **优点** - 下载体积小,速度快,节省流量。 - 用户体验更友好(尤其对大应用)。 #### **缺点** - 实现复杂,需处理合并失败、版本回退等异常。 - 维护成本高(需为每个旧版本生成补丁)。 --- ### **3. 对比与选型建议** | **场景** | **推荐策略** | |------------------------|-------------------| | 应用体积小,更新频率低 | 全量更新 | | 应用体积大,需频繁更新 | 增量更新 | | 跨平台兼容性要求高 | 全量更新(更稳定) | | 服务器资源有限 | 增量更新 | --- ### **4. 结合 Vue 的注意事项** 1. **渲染进程更新** - Vue 作为前端页面,可通过重新加载页面或替换静态资源(如 HTML/JS/CSS)实现更新,但需与 Electron 主进程版本管理分离。 2. **代码分离** -主进程代码(Electron渲染进程代码(Vue)分开打包,可独立更新渲染层(如通过 CDN 加载最新前端资源)。 --- ### **5. 安全与回滚** - **校验文件完整性**:使用哈希(如 SHA256)验证下载包。 - **回滚机制**:保留旧版本安装包,更新失败时自动回退。 --- 通过合理选择全量增量更新策略,可以显著提升 Electron + Vue 应用的更新效率用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

中二少年学编程

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

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

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

打赏作者

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

抵扣说明:

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

余额充值