使用electron-updater进行全量更新

1,090 阅读5分钟

render层与main层的通信

使用ipcRenderer进行通信,为electron自带对象。使用方式类似于事件通信

检查更新

思路

  1. 安装依赖
  2. 编写renderer层和main层的检查更新逻辑,autoupdater提供了热更新的方法
  3. 设置package和build.json等

1. 安装依赖

注意,需要安装在生产环境(dependencies)中。因为是在应用使用中会使用的功能

npm install electron-updater

2. 编写逻辑

思路:

  1. 编写renderer层和main层的通信,当页面点击检查更新时通信给main层进行检查。检查过程中出现进度信息再通信给renderer层。
  2. 编写检查更新的逻辑。同时还有退出更新的逻辑
  3. 进行测试
  1. 编写通信方法

    • renderer层使用electron的ipcRenderer对象进行监听和发送

    src/renderer/utils/ipcRenderer.ts

    //* 用于向main层通信的封装方法
    const { ipcRenderer } = require("electron"); //* vite引入electron需要使用requireexport function send(code: string) { //* 发送事件
        ipcRenderer.invoke(code)
    }
    ​
    export function on(code: string, callback: (event, age: unknown) => void) { //* 监听事件
        return ipcRenderer.on(code, callback)
    }
    ​
    export default {
        send,
        on
    }
    
  • main层使用electron的ipcMain进行监听,使用mainWindow的webContents进行发送

src/main/services/ipcMain.ts 用于监听事件

   //* 用于管理main层的监听工具类
   import { ipcMain } from "electron"
   import AppUpdate from './checkUpdate'
   
   //* 引入工具类
   import { getMainWindowByIpcEvent } from '../utils/MainWindow'
   
   let appUpdate = null
   
   function onEvent() { //* 监听从rendener层传送过来的数据
       ipcMain.handle('check-update', (event) => { //* 监听检查更新事件
           appUpdate.checkUpdate(getMainWindowByIpcEvent(event))
       })
       ipcMain.handle('confirm-update', () => {
           appUpdate.quitAndInstall()
       })
   }
   
   export function Mainfunc() {
       //* 初始化对象 用于检查更新 下方会使用到
       appUpdate = new AppUpdate()
       //* 监听事件
       onEvent()
   }
   
   //* 用于管理ipc通信
   export default  {
       
   }

src/main/utils/MainWindow.ts 工具类,提供获取mainwindow的方法和发送信息的方法

   //* 关于mainWindow的工具类
   
   import { BrowserWindow, IpcMainInvokeEvent } from "electron";
   
   //* 根据Ipc获取mainWindow
   export function getMainWindowByIpcEvent(event: IpcMainInvokeEvent) {
     return BrowserWindow.fromWebContents(event.sender);
   }
   
   //* 发送信息给渲染层(vue)
   export function sendToRender(
     mainWindow: BrowserWindow | undefined | null,
     code: string,
     type: number,
     data?: string
   ) {
     if(!mainWindow) {
       return
     }
     const senddata = {
       state: type,
       msg: data || "",
     };
      mainWindow.webContents.send(code, senddata);
   }

2. 编写逻辑

  1. renderer层在点击检查更新时向main层发送通信。并且监听main层发送来的消息。比如无更新,更新下载进度等。以便做出渲染

src/renderer/pages/index/index.vue

   <template>
     <h1>更新界面111</h1>
     <div class="actions">
       <el-button @click="checkUpdate">
         检查更新
       </el-button>
     </div>
     <el-dialog
       v-model="dialogVisible"
       title="进度"
       center
       width="14%"
       top="45vh"
     >
       <div class="conten">
         <el-progress
           type="dashboard"
           :percentage="percentage"
           :color="colors"
           :status="progressStaus"
         />
       </div>
     </el-dialog>
   </template>
   <script setup lang="ts">
   import { ref } from 'vue'
   
   import { send, on } from '@renderer/utils/ipcRenderer'
   import { messageByText, successMessage } from '@renderer/utils/El'
   
   let percentage = ref(0);
   let colors = ref([
     { color: "#f56c6c", percentage: 20 },
     { color: "#e6a23c", percentage: 40 },
     { color: "#6f7ad3", percentage: 60 },
     { color: "#1989fa", percentage: 80 },
     { color: "#5cb87a", percentage: 100 },
   ]);
   
   let dialogVisible = ref(false);
   let progressStaus = ref('');
   
   function checkUpdate() {
     //* 检查更新方法 发送通信
     send('check-update') //通信到main层
   }
   
   //* 监听main层发送过来的数据
   on('UpdateMsg', (event, age): void => {
     console.log(event, age)
     switch (age.state) {
       case -1:
         console.log('发生错误')
         break
       case 0:
         messageByText("正在检查更新");
         break;
       case 1:
         successMessage("已检查到新版本,开始下载");
         dialogVisible.value = true;
         break;
       case 2:
         successMessage("无新版本");
         break;
       case 3:
         percentage.value = age.msg.percent.toFixed(1);
         break;
       case 4:
         console.log('添加setTimeout事件')
         setTimeout(() => {
           messageByText('退出更新')
           //发送退出并更新的通信 可写自己逻辑,比如询问是否立即更新等
           send('confirm-update')
         }, 5000);
         progressStaus.value = "success";
         successMessage('下载完成')
         break;
       default:
         break;
     }
   })
   </script>
  1. main层方法

src/main/services/checkUpdate.ts

   //* 检查更新工具类
   import { autoUpdater } from "electron-updater";
   import { BrowserWindow } from 'electron';
   
   //* 引入工具类
   import { sendToRender } from '../utils/MainWindow';
   import path from "path";
   
   class CheckUpdate {
     public mainWindow: BrowserWindow | undefined | null
     constructor() {
       //* 设置检查更新的url 可以不设置 忽略
       // autoUpdater.setFeedURL("http://127.0.0.1:5500/");
       //* 修改配置地址 dev使用 开发环境需要注释 根据自己路径来,需要获取app-update.yml
       autoUpdater.updateConfigPath = path.join(__dirname, '../../../build/win-unpacked/resources/app-update.yml')
       this.updaterEvent()
     }
     checkUpdate(mainWindow: BrowserWindow | null) { //* 开始检查更新
       //* 赋值当前需要检查更新的窗口
       this.mainWindow = mainWindow
       autoUpdater.checkForUpdates().catch(err => {
         console.log('网络连接问题', err)
       })
     }
     // 退出并安装
     quitAndInstall() {
       autoUpdater.quitAndInstall()
     }
     updaterEvent() { //* 监听updater的事件
       /**
        * -1 检查更新失败 0 正在检查更新 1 检测到新版本,准备下载 2 未检测到新版本 3 下载中 4 下载完成
        **/
       // 当开始检查更新的时候触发
       autoUpdater.on("checking-for-update", (event, arg) => {
         console.log("开始检查更新", event, arg);
         sendToRender(this.mainWindow,'UpdateMsg', 0)
       });
   
       // 发现可更新数据时
       autoUpdater.on("update-available", (event, arg) => {
         console.log("有更新", event, arg);
         sendToRender(this.mainWindow,'UpdateMsg', 1)
       });
   
       // 没有可更新数据时
       autoUpdater.on("update-not-available", (event, arg) => {
         console.log("没有更新", event, arg);
         sendToRender(this.mainWindow, 'UpdateMsg', 2)
       });
   
       // 下载监听
       autoUpdater.on("download-progress", (progressObj) => {
         console.log(progressObj, '下载监听')
         sendToRender(this.mainWindow,'UpdateMsg', 3, progressObj)
       });
   
       // 下载完成
       autoUpdater.on("update-downloaded", () => {
         console.log("下载完成");
         sendToRender(this.mainWindow,'UpdateMsg', 4)
       });
       // 当更新发生错误的时候触发。
       autoUpdater.on('error', (err) => {
         console.log('更新出现错误', err.message)
         if (err.message.includes('sha512 checksum mismatch')) {
           sendToRender(this.mainWindow, -1, 'sha512校验失败')
         } else {
           sendToRender(this.mainWindow, -1, '错误信息请看主进程控制台')
         }
       })
     }
   }
   
   export default CheckUpdate

从这里可以再去看看上面通信层,会调用到checkUpdate的方法

build.json:修改build.json的同时package.json的启动命令也需要修改,详情可以看我写的入门文章

   {
       "publish": [// autoUpdate检查更新的配置
       {
         "provider": "generic",
         "url": "http://127.0.0.1:5500/" // 为资源地址的路径
       }
     ],
   }

3. 进行测试

  1. 可以使用vscode的Live Server插件创建一个本地文件服务器。
  2. 修改package.json的version属性(比当前版本高)并进行打包
  3. 将打包后的build文件夹下面的文件全部复制到资源文件夹路径下面(autoUpdate会检查latest.yml比对版本,如果需要更新下载则下载exe文件)

  1. vite引入require 需要引入依赖,并且在tsconfig.json中配置
  2. dev环境下报不能检查的错误
  1. 引入依赖为@types/node

    配置tsconfig.json

    "types": [ "vite/client"], //** 设置vite兼容(vite-client)用于使用require
    
  2. dev环境下报不能检查的错误

    src/main/index.ts

    import { app } from 'electron'
    // 生产环境下需要注释
    Object.defineProperty(app, 'isPackaged', {
      get() {
        return true;
      }
    });
    
api
  • .checkForUpdates() 执行一次检查更新
  • .checkForUpdatesAndNotify() 执行一次检查更新,如果有新的可用更新,还会自定弹出一个自带的通知提示告诉用户有新的更新
  • .downloadUpdate(cancellationToken) 执行下载安装包
  • .quitAndInstall(isSilent, isForceRunAfter) 退出应用并安装更新, isSilent 是否静默更新,isForceRunAfter更新完后是否立即运行
事件
  • error 检查更新错误
  • checking-for-update 正在检查更新
  • update-available 有新的可用更新
  • update-not-available 没有可用的更新,也就是当前是最新版本
  • download-progress 正在下载更新版,会有更新进度对象
  • update-downloaded 新安装包下载完成
配置项
  • autoDownload = true 有可用更新时是否自动下载
  • autoInstallOnAppQuit = true 如果安装包下载好了,那么当应用退出后是否自动安装更新
  • allowPrerelease = false 是否接受开发版,测试版之类的版本号
  • allowDowngrade = false 是否可以回退版本,比如从开发版降到旧的稳定版

源码地址