Skip to content

Electron 更新

更新方式

  • 全量更新

全量更新指的是 用户从头到尾重新安装一遍 .工作原理是:他会去线上匹配 yml 文件里面的版本号,

当你的 package.json 里面的版本号要是小于线上的版本号的时候,就会提示用户更新,然后用户点击更新的时候,就会去下载最新的安装包,然后安装,安装完成之后,会自动打开应用,然后关闭旧版本的应用

  • 增量更新

增量更新指的是 用户只更新了部分文件,而不是从头到尾重新安装一遍 .工作原理是:他会去线上匹配 asr 文件 .它只是下载了 asr 文件 然后匹配(这里涉及了权限问题)

全量更新

第一步:

把新版本的安装包放到服务器上.重点是 yml 文件必须有

第二步:

  • 找到 electron 文件夹下面的 builder.json 文件,修改这个里面的地址
ts
  "publish": [
    {
      "provider": "generic",
      "url": "https://www.dianyuan.com/upload/electron"
    }
  ],
  • 找到 config 文件夹下面的 config.default.js 文件,增加更新包路径
ts
"use strict";

const path = require("path");

/**
 * 默认配置
 */
module.exports = (appInfo) => {
  const config = {};
  /**
   * 开发者工具
   */
  config.openDevTools = false;

  // 假设打包
  config.isPackaged = true;

  /**
   * 应用程序顶部菜单
   */
  config.openAppMenu = true;

  /**
   * 主窗口
   */
  config.windowsOption = {
    title: "EE框架",
    width: 980,
    height: 650,
    minWidth: 400,
    minHeight: 300,
    webPreferences: {
      //webSecurity: false,
      contextIsolation: false, // false -> 可在渲染进程中使用electron的api,true->需要bridge.js(contextBridge)
      nodeIntegration: true,
      //preload: path.join(appInfo.baseDir, 'preload', 'bridge.js'),
    },
    frame: true,
    show: false,
    icon: path.join(appInfo.home, "public", "images", "logo-32.png"),
  };

  /**
   * ee框架日志
   */
  config.logger = {
    encoding: "utf8",
    level: "INFO",
    outputJSON: false,
    buffer: true,
    enablePerformanceTimer: false,
    rotator: "day",
    appLogName: "ee.log",
    coreLogName: "ee-core.log",
    errorLogName: "ee-error.log",
  };

  /**
   * 远程模式-web地址
   */
  config.remoteUrl = {
    enable: false,
    url: "http://electron-egg.kaka996.com/",
  };

  /**
   * 内置socket服务
   */
  config.socketServer = {
    enable: false,
    port: 7070,
    path: "/socket.io/",
    connectTimeout: 45000,
    pingTimeout: 30000,
    pingInterval: 25000,
    maxHttpBufferSize: 1e8,
    transports: ["polling", "websocket"],
    cors: {
      origin: true,
    },
    channel: "c1",
  };

  /**
   * 内置http服务
   */
  config.httpServer = {
    enable: false,
    https: {
      enable: false,
      key: "/public/ssl/localhost+1.key",
      cert: "/public/ssl/localhost+1.pem",
    },
    host: "127.0.0.1",
    port: 7071,
    cors: {
      origin: "*",
    },
    body: {
      multipart: true,
      formidable: {
        keepExtensions: true,
      },
    },
    filterRequest: {
      uris: ["favicon.ico"],
      returnData: "",
    },
  };

  /**
   * 主进程
   */
  config.mainServer = {
    protocol: "file://",
    indexPath: "/public/dist/index.html",
  };

  /**
   * 硬件加速
   */
  config.hardGpu = {
    enable: true,
  };

  /**
   * 异常捕获
   */
  config.exception = {
    mainExit: false,
    childExit: true,
    rendererExit: true,
  };

  /**
   * jobs
   */
  config.jobs = {
    messageLog: true,
  };

  /**
   * 插件功能
   */
  config.addons = {
    window: {
      enable: true,
    },
    tray: {
      enable: true,
      title: "EE程序",
      icon: "/public/images/tray.png",
    },
    security: {
      enable: true,
    },
    awaken: {
      enable: true,
      protocol: "ee",
      args: [],
    },
    autoUpdater: {
      enable: true,
      windows: false,
      macOS: false,
      linux: false,
      options: {
        provider: "generic",
        url: "http://kodo.qiniu.com/",
      },
      force: false,
    },
  };

  /* 更新包路径 */
  config.autoUpdatePath = "https://www.dianyuan.com/upload/electron";

  return {
    ...config,
  };
};
  • 第三步: 在 electron 文件夹下面新建一个 utils 文件夹,然后新建一个 autoUpdater.js 文件
ts
const { dialog } = require("electron");
const { autoUpdater } = require("electron-updater");

//自动下载更新
autoUpdater.autoDownload = false;
//退出时自动安装更新
autoUpdater.autoInstallOnAppQuit = false;
// 测试必须开启
autoUpdater.forceDevUpdateConfig = true;

module.exports = (event, path) => {
  // 设置更新源url
  autoUpdater.setFeedURL(path);
  //检查是否有更新
  autoUpdater.checkForUpdates();

  //有新版本时
  autoUpdater.on("update-available", (_info) => {
    dialog
      .showMessageBox({
        type: "warning",
        title: "更新提示",
        message: "有新版本发布了",
        buttons: ["更新", "取消"],
        cancelId: 1,
      })
      .then((res) => {
        if (res.response == 0) {
          //开始下载更新
          console.log("2222");
          autoUpdater.downloadUpdate();
        }
      });
  });

  //没有新版本时
  autoUpdater.on("update-not-available", (_info) => {
    console.log("没有更新");
  });

  //更新下载完毕
  autoUpdater.on("update-downloaded", (_info) => {
    //退出并安装更新
    autoUpdater.quitAndInstall();
  });

  //更新发生错误
  autoUpdater.on("error", (_info) => {
    console.log("更新时发生错误");
  });

  // 监听下载进度
  autoUpdater.on("download-progress", (progress) => {
    console.log(JSON.stringify(progress));
    event.reply("controller.example.onlistener", progress);
  });
};

分层

  • 视图层
html
<template>
  <div>
    <button @click="checkForUpdates('all')">全局更新</button>
    <button @click="checkForUpdates('part')">局部更新</button>
    <el-progress
      :text-inside="true"
      :stroke-width="24"
      :percentage="percentage"
      status="success"
    />
  </div>
</template>

<script setup>
  import { ipc } from "@/utils/ipcRenderer";
  import { ipcApiRoute } from "@/api/main.js";
  import { onMounted, ref } from "vue";
  const percentage = ref(0);
  // 开启监听

  onMounted(() => {
    ipc.on("controller.example.onlistener", (event, result) => {
      percentage.value = parseInt(result.percent);
    });
  });

  const checkForUpdates = (data) => {
    if (data == "all") {
      ipc.send(ipcApiRoute.allupdate);
    } else {
      ipc.send(ipcApiRoute.partupdate);
    }
  };
</script>

<style lang="scss" scoped></style>
  • 控制层

这里特别注意 event 就是第二个参数,必须要传递给 Services 层

ts
"use strict";

const { Controller } = require("ee-core");
const Log = require("ee-core/log");
const Services = require("ee-core/services");

/**
 * example
 * @class
 */
class ExampleController extends Controller {
  constructor(ctx) {
    super(ctx);
  }

  /**
   * 所有方法接收两个参数
   * @param args 前端传的参数
   * @param event - ipc通信时才有值。详情见:控制器文档
   */

  /**
   * test
   */
  async test() {
    const result = await Services.get("example").test("electron");
    Log.info("service result:", result);

    return "hello electron-egg";
  }
  /*全局增量*/
  async allupdate(args, event) {
    await Services.get("example").allupdate(event);
  }
  /* 增量更新 */
  async partupdate(event) {
    await Services.get("example").partupdate(event);
  }
}

ExampleController.toString = () => "[class ExampleController]";
module.exports = ExampleController;
  • Services 层
ts
"use strict";
const autoUpdater = require("../utils/autoUpdater");
const { Service } = require("ee-core");

/**
 * 示例服务(service层为单例)
 * @class
 */
class ExampleService extends Service {
  constructor(ctx) {
    super(ctx);
  }

  /*全局增量*/
  async allupdate(event) {
    console.log("quanjuzengliang");
    // const { app } = this;
    // const mainWindow = app.electron.mainWindow;
    // const win = mainWindow.webContents;
    console.log(this.config.autoUpdatePath);
    const path = this.config.autoUpdatePath;
    autoUpdater(event, path);
  }
  /* 增量更新 */
  async partupdate() {
    console.log("点击了局部更新");
    return "点击了局部更新";
  }
}

ExampleService.toString = () => "[class ExampleService]";
module.exports = ExampleService;