electron 增量更新

2,846 阅读3分钟

项目有涉及到electron更新,对于electron更新可谓是踩了不是坑。

electron的更新一般来说有两种方式,全量和增量,顾名思义全量就是下载我们打包好的exe文件或者zip文件,进行全面替换。我们之前说过electron就是用浏览器打开我们的页面,很多时候我们的更新可能只会修改渲染进程,那么我们把我们的渲染进程的文件给替换了不久更新吗,也就是说增量实际上是替换打包好的html,js等文件。那么更新的方式如下:

  • 主进程修改:全量更新
  • 渲染进程修改:全量或增量更新

而我开始对这个并没有很深刻的认识,看了几遍文章一顿瞎搞,结果搞得是全量更新,看着110mb大小的exe下载当时就麻木了这要是用户能接受吗?

全量更新可以参考 juejin.cn/post/705481… 然后稍微自已处理下,这个文章讲的是非常清晰了。 增量更新:当时看了很多文章,整体逻辑呢接收服务器放更新的代码,本地负责请求下载对比版本号是否有更新一系列操作,废话不多说直接上代码(当时参考了一位CSDN老哥的自已做了修改) 首上创建个.ts文件封装好更新的方法 还有一些用得上的插件比如adm-zip包

const admZip = require("adm-zip");
const request = require("request");
const fs = require("fs");
const path = require("path");
const baseUrl = path.resolve("./") + "/resources/";
const fileUrl = ""; //这里需要修改为自己的资源外网
import { app, ipcMain} from "electron";
/**
 * 更新
 */
const downLoad = () => {
  return new Promise<boolean>((resolve, reject) => {
    // 创建一个可以写入的流,
    const stream = fs.createWriteStream(`${baseUrl}app.zip`);
    const url = `${fileUrl}app.zip`;
    request(url)
      .pipe(stream)
      .on("close", () => {
        const unzip = new admZip(`${baseUrl}app.zip`); //下载压缩更新包
        unzip.extractAllTo(`${baseUrl}`, true); //解压替换本地文件
        resolve(true);
      });
  });
};
const CheckForUpdates = () => {
  return new Promise((resolve, reject) => {
    request(
      {
        url: `${fileUrl}package.json`, //请求package.json,与本地对比版本号
      },
      (error: any, res: any, body: any) => {
        try {
          if (error || res.statusCode !== 200) {
            throw "Failed to update the version number, please contact the administrator";
          }
          const json = JSON.parse(body);
          const { version, description } = json;
          console.log(version);
          
          /**
           * app.getVersion() 返回开发中的 Electron 版本号
           */
          const localVersion = app.getVersion();
          let flag: boolean = false;
          version !== localVersion ? (flag = true) : (flag = false);
          ipcMain.on("exist_update", (event, message) => {
            event.returnValue = flag;
          });
          //ipc通信 确认更新下载
          ipcMain.handle("new_update", async (event, message) => {
            let flag = await downLoad();
            return flag;
          });
          ipcMain.on("Sure", (event, message) => {
            app.exit();
            app.relaunch();
          });
        } catch (err) {
          console.log(err);
          reject(err);
        }
      },
    );
  });
};
export { downLoad, CheckForUpdates };

然后主进程引入这个ts文件的方法 调用就可以了 例如:

import {
  CheckForUpdates
}from '@/util/renew' //这是引入的方法
async function createWindow() {
    // Create the browser window.
    Menu.setApplicationMenu(null); //隐藏菜单
    win = new BrowserWindow({
      width: 1000,
      height: 600,
      center: true, // 默认居中
      frame: false,
      transparent: true,
      resizable: false,
      webPreferences: {
        nodeIntegration: true, // 使用node
        contextIsolation: false,
        enableRemoteModule: true,
      },
    });
   win.webContents.openDevTools();//控制台
   CheckForUpdates();

就不展示完了

渲染进程

我是用了element-ui-plus 定义个方法里面去做具体的逻辑每个人都都不一样,我是需要进来这个页面就去获取一遍是否需要更新的弹窗··· 然后生命周期调用

     click() {
        //发送通知是否存在更新
        let a = ipcRenderer.sendSync("exist_update");
        console.log(a);
        if (a) {
          //有更新
          state.renew = 1;
          ElMessageBox.confirm(
            lang.systemLang.update_version,
            lang.systemLang.update_prompt,
            {
              confirmButtonText: lang.systemLang.Confirm,
              cancelButtonText: lang.systemLang.Cancel,
              type: "warning",
            },
          )
            .then(() => {
              ipcRenderer.invoke("new_update").then((res) => {
                if (res) {
                  ElMessageBox.confirm(
                    lang.systemLang.update_completed,
                    lang.systemLang.reboot,
                    {
                      confirmButtonText: lang.systemLang.Confirm,
                      cancelButtonText: lang.systemLang.Cancel,
                      type: "warning",
                    },
                  )
                    .then((res) => {
                      ipcRenderer.send("Sure");
                    })
                    .catch((err) => {});
                }
              });
            })
            .catch(() => {
              //取消
            });
        } else {
          //没有更新
          state.renew = 2;
        }
      },
    });
    onMounted(() => {
      state.click();
    });

哦 对了打包还需要设置 vue.config.js 我是用的asar true 打出来的包 默认是false app文件夹的那种(有个小问题之前问过大佬 是说打包完有app.asar优先使用app.asar 所以我就直接打asar的包不需要app的)

asar: true,

正常打包electron客户端后,把这个app.asar文件压缩,放到服务器上,还有package.json文件也要放上去。
adm-zip需要手动安装 npm install adm-zip --save 好了 基本核心的就是这些东西了。有什么不懂的欢迎留言