Electron:软件更新

本文介绍了使用Electron的electron-updater模块实现客户端自动更新的详细步骤,包括启动时检查更新、比对版本、下载进度显示、安装更新等,并提供了update.js和index.js的关键代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注:本人初学记录,用法并非最优,仅供参考。

一、更新流程

1、客户端启动,调用服务器接口,获取是否有更新配置(服务端返回发布的最新版本号和更新内容)。

2、客户端比对当前版本和服务端接口返回版本是否一致,若版本号大于本地版本,弹出升级提示。

3、用户点击升级,页面显示下载进度、下载完毕自动更新、重启。

二、update.js,需要配置updateUrl,既软件安装包所在服务器的目录,如http://域名/app/,其中app目录下存放的latest.yml、安装包.exe(mac端是latest-mac.yml、安装包.dmg、安装包.zip)

const { ipcMain } = require('electron')
const { autoUpdater } = require('electron-updater')

// 更新地址,该地址下放的是安装包和latest.yml
const updateURL = 'http://你的软件下载网络地址/'

const message = {
  error: '软件更新异常,请重试',
  checking: '正在检查更新',
  updateAva: '检测到新版本,准备下载',
  updateDown: '软件下载中,请耐心等待',
  updateSet: '下载完成,准备安装',
  updateNotAva: '已经是最新版本',
}

//软件版本更新
ipcMain.handle('on-soft-update', (e) => {
  autoUpdater.checkForUpdates()
})

// 检测更新,在你想要检查更新的时候执行,renderer事件触发后的操作自行编写
function handleUpdate(mainWindow, callback) {
  // 设置是否自动下载,默认是true,当点击检测到新版本时,会自动下载安装包,所以设置为false
  autoUpdater.autoDownload = true

  // 如果安装包下载好了,当应用退出后是否自动安装更新
  autoUpdater.autoInstallOnAppQuit = false

  // 设置版本更新服务器地址
  autoUpdater.setFeedURL(updateURL)

  // 更新发生错误时触发
  autoUpdater.on('error', function () {
    sendUpdateMessage(message.error)
  })

  // 开始检查更新事件
  autoUpdater.on('checking-for-update', function () {
    sendUpdateMessage(message.checking)
  })

  // 没有可更新版本
  autoUpdater.on('update-not-available', function (info) {
    sendUpdateMessage(message.updateNotAva)
  })

  // 发现可更新版本
  autoUpdater.on('update-available', function (info) {
    sendUpdateMessage(message.updateAva)
  })

  // 更新下载进度事件
  autoUpdater.on('download-progress', function (progressObj) {
    sendUpdateMessage(message.updateDown)
    mainWindow.webContents.send('on-soft-download', progressObj.percent)
  })

  // 下载监听
  autoUpdater.on(
    'update-downloaded',
    function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
      mainWindow.webContents.send('on-soft-download', 100)
      sendUpdateMessage(message.updateSet)
      //3秒后更新
      setTimeout(() => {
        autoUpdater.quitAndInstall()
      }, 3000)
    }
  )

  // 向渲染进程发送消息
  function sendUpdateMessage(text) {
    mainWindow.webContents.send('on-soft-message', text)
  }
}

module.exports = {
  handleUpdate,
}

三、index.js ,既electron主进程的启动文件

const { app, BrowserWindow, Tray, Menu, nativeImage } = require('electron')
const autoUpdate = require('./update.js')
const path = require('path')

//防止应用多开
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
  app.quit()
} else {
  //创建托盘
  const createTray = (win) => {
    const trayMenu = [{ label: '我的软件', role: 'quit' }]
    const tray = new Tray(nativeImage.createFromPath(path.join(__dirname, 'favicon.ico')))
    tray.setToolTip('我的软件')
    tray.on('click', (e) => {
      win.isVisible() ? win.hide() : win.show()
    })
    tray.setContextMenu(Menu.buildFromTemplate(trayMenu))
  }
    
  //创建窗口
  const createWindow = () => {
    const mainWindow = new BrowserWindow({
      width: 760,
      height: 500,
      center: true,
      minWidth: 760,
      minHeight: 500,
      useContentSize: false,
      backgroundColor: '#00000000',
      hasShadow: true,
      icon: path.join(__dirname, 'logo.png'),
      webPreferences: {
        nodeIntegration: false,
        contextIsolation: true,
        preload: path.join(__dirname, 'preload.js'),
      },
      show: false,
    })
    
    //加载页面,略,mainWindow.loadURL 或 mainWindow.loadFile
    
    //加载完毕打开窗口
    mainWindow.on('ready-to-show', () => {
      autoUpdate.handleUpdate(mainWindow)
      mainWindow.show()
    })
      
    //创建托盘
    createTray(mainWindow)
  }
  
  //报错:ERROR:gpu_init.cc(481)] 修复,禁用硬件加速
  app.disableHardwareAcceleration()

  //屏蔽安全告警在console控制台的显示
  process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'

  app.whenReady().then(() => {
    createWindow()
    app.on('activate', () => {
      if (BrowserWindow.getAllWindows().length === 0) createWindow()
    })
  })

  app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') app.quit()
  })
    
}

四、preload.js针对进度加载,由于页面不能使用渲染进程remote,这里使用ipc方式

//更新软件
const softUpdate = () => {
  ipcRenderer.invoke('on-soft-update')
}

//更新信息
let message = ''
ipcRenderer.on('on-soft-message', (e, arg) => {
  message = arg
})
const upInfo = () => {
  return message
}

//更新下载进度
let progress = 0
ipcRenderer.on('on-soft-download', (e, arg) => {
  progress = arg
})
const upProgress = () => {
  return progress
}

//定义所有的接口方法
contextBridge.exposeInMainWorld('myApi', {
  upInfo,
  upProgress,
  softUpdate,
})

五、更新弹出框SoftUpdate.vue

<template>
  <el-dialog
    v-dialogDrag
    title="软件升级"
    width="550px"
    :visible.sync="isVisible"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    @close="handleClose"
  >
    <el-card class="page-header" shadow="hever">
      <!--新的版本,当有新版本时候显示-->
      <div class="page-header-up">
        <template>
          <p class="page-header-up-title">更新进度:{{ upInfo }}</p>
          <el-progress :text-inside="true" :stroke-width="25" :percentage="percentage" />
          <el-divider />
        </template>
        <div style="float: right; margin-top: 5px">
          <el-button type="primary" @click="updateVersion" size="mini">更新</el-button>
          <el-button @click="handleClose" size="mini">关闭</el-button>
        </div>
      </div>
    </el-card>
  </el-dialog>
</template>

<script>
  export default {
    name: 'SoftUpdate',
    data() {
      return {
        isVisible: false,
        upInfo: '',
        percentage: 0,
      }
    },

    //生命周期若已设置定时器,需清空定时器beforeDestroy()
    beforeDestroy() {
      clearInterval(this.refreshData)
    },

    methods: {
      /**
       * 打开
       */
      showDialog() {
        this.isVisible = true
      },

      //更新
      async updateVersion() {
        myApi.softUpdate()
        this.upInfo = ''
        this.percentage = 0
        if (this.refreshData == null) {
          this.refreshData = setInterval(() => {
            this.upInfoData()
          }, 1000)
        }
      },

      //更新进度
      upInfoData() {
        this.upInfo = myApi.upInfo()
        this.percentage = parseInt(myApi.upProgress())
      },

      /**
       * 关闭
       */
      handleClose() {
        clearInterval(this.refreshData)
        this.upInfo = ''
        this.percentage = 0
        this.isVisible = false
      },
    },
  }
</script>

<style lang="scss" scoped>
  ::v-deep .el-dialog {
    background-color: #f1f3f4;
  }
  ::v-deep .el-dialog__header {
    position: relative;
    padding: 6px;
    background-color: #fff;
  }
  ::v-deep .vab-theme-blue-black .el-dialog__headerbtn {
    margin-top: -7px;
    background: 0 0;
    border: none;
  }
  ::v-deep .el-dialog__body {
    padding: 0px;
    font-size: 12px;
    color: #f1f3f4;
    word-break: break-all;
  }

  .page-header {
    background: #f7ffffbd; //f8fff7bd
    transition: none;

    &-up {
      flex: auto;
      margin: 0 20px 45px 20px;

      &-title {
        font-size: 14px;
        color: #1890ff;
      }

      &-description {
        font-size: 12px;
        color: #808695;
      }
    }
  }
</style>

六、package.json配置

 "publish": [
          {
            "provider": "generic",
            "url": ""             //半自动更新,所以这里没加的地址
          }
        ]

七、需要自己实现(比如页面启动调用服务端接口获取最新版本,比对当前版本和服务器最新版本,然后弹出SoftUpdate.vue)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值