Skip to content

Electron 自动更新

Posted on:2021年3月3日 at 14:41

1. 安装 electron-updater

npm i electron-updater --save-dev

2. 在 package.json 中

"build": {
    // ...
    "win": {
      "icon": "build/icons/icon.ico",
      "asar": false,
      "artifactName": "${productName}_${version}.${ext}"
    },
    "publish": [
      {
        "provider": "generic",
        "url": "http://127.0.0.1:8080" // 你的远程服务器更新文件地址,暂时用本地服务模拟
      }
    ],
    "releaseInfo": {
      "releaseNotes": "" // 更新的内容,可不填
    }
  }

3. update.js

import { autoUpdater } from "electron-updater";
import { ipcMain } from "electron";

// 开始检查更新
ipcMain.on("checkForUpdates", () => autoUpdater.checkForUpdates());

// 开始下载更新
ipcMain.on("downLoadUpdate", () => autoUpdater.downloadUpdate());

const updateHandle = (mainWindow, updateURL) => {
  const webContents = mainWindow.webContents;
  const statusMessage = {
    error: { status: -1, msg: "检测更新查询异常" },
    checking: { status: 0, msg: "正在检查应用程序更新" },
    updateAva: { status: 1, msg: "检测到新版本,正在下载,请稍后" },
    updateNotAva: { status: 2, msg: "您现在使用的版本为最新版本,无需更新!" },
    downloadSuccess: { status: 2, msg: "下载新版成功" },
  };

  autoUpdater.autoDownload = false; // 手动指定下载
  autoUpdater.setFeedURL(updateURL); // 更新包的地址,如 https://xxx.com/app/

  //执行自动更新检查
  // autoUpdater.checkForUpdates()

  //更新错误
  autoUpdater.on("error", error => {
    console.log(error);
    webContents.send("uploadMessage", {
      payload: statusMessage.error,
      output: error,
    });
  });

  //检查中
  autoUpdater.on("checking-for-update", v => {
    console.log("检查中");
    webContents.send("uploadMessage", {
      payload: statusMessage.checking,
      output: v,
    });
  });

  //发现新版本
  autoUpdater.on("update-available", info => {
    console.log("发现新版本");
    webContents.send("uploadMessage", {
      payload: statusMessage.updateAva,
      output: info,
    });
  });

  //当前版本为最新版本
  autoUpdater.on("update-not-available", info => {
    console.log("当前版本为最新版本");
    webContents.send("uploadMessage", {
      payload: statusMessage.updateNotAva,
      output: info,
    });
  });

  // 更新下载进度事件
  autoUpdater.on("download-progress", progress =>
    webContents.send("downloadProgress", progress)
  );

  // 当下载完更新包后触发
  autoUpdater.on("update-downloaded", info => {
    webContents.send("uploadMessage", {
      payload: statusMessage.downloadSuccess,
      output: info,
    });
    autoUpdater.quitAndInstall();
  });
};

export default updateHandle;

4. main 进程中

const packInfo = require("../../package.json");
const [publish] = packInfo.build.publish;

import updateHandle from "./update";

// ....略
// createWindow时候
updateHandle(mainWindow, publish.url);

5. 渲染进程中(vue)

<template>
  <el-dialog
    style="pointer-events: none"
    :center="true"
    title="版本正在更新,请稍候..."
    :visible.sync="visible"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    :show-close="false"
  >
    <div>
      <el-progress
        :text-inside="true"
        :stroke-width="24"
        :percentage="percentage"
        :show-text="true"
      />
    </div>
  </el-dialog>
</template>

<script>
  const { ipcRenderer } = require("electron");

  export default {
    name: "AutoUpdate",
    data() {
      return {
        visible: false,
        percentage: 0,
      };
    },
    mounted() {
      this.init();
    },
    methods: {
      init() {
        ipcRenderer.send("checkForUpdates");
        ipcRenderer.on("uploadMessage", (event, args) => {
          console.log(args);
          const { payload } = args;
          const { msg, status } = payload;
          const handle = {
            "-1": () => {
              this.$message.error(msg);
            },
            0: () => {
              this.$message.info(msg);
              /*正在检测更新*/
            },
            1: () => {
              this.$message.info(msg);
              /* 发送下载请求 */
              ipcRenderer.send("downLoadUpdate");
              this.visible = true;
            },
            2: () => {
              this.$message.info(msg);
              /*当前为最新版本*/
            },
            3: () => {
              this.$message.success(msg);
            },
          };
          handle[`${status}`].call(this);
        });

        ipcRenderer.on("downloadProgress", (event, data) => {
          const { percent } = data;
          this.percentage = Number.parseFloat((percent || 0).toFixed(2));
          if (percent >= 100) this.visible = false;
        });
      },
    },
  };
</script>

6. 测试

使用 node 静态托管,nginx 也可以

const express = require("express");
const app = express();

//设置跨域访问
app.all("*", (req, res, next) => {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "X-Requested-With");
  res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
  res.header("X-Powered-By", " 3.2.1");
  res.header("Content-Type", "application/json;charset=utf-8");
  next();
});

app.use(express.static("D:/work/code/electron-print-web/build")); // 此处为打包后生成文件的绝对路径

const server = app.listen(8080, "localhost", () => {
  const host = server.address().address;
  const port = server.address().port;
  console.log("Example app listening at http://%s:%s", host, port);
});