electron 自动更新 热跟新

本文介绍了一种使用electron-hot-updata插件实现Electron应用热更新的方法,避免了传统自动更新后需重新安装的问题,提升了用户体验。通过设置服务器地址、版本号及权限等步骤,配合特定脚本和插件实现自动检查并下载更新,最后动态替换执行文件。

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

使用electron-upadta 自动跟新之后需要用户在重新安装一遍程序,用户体验不好,开发electron-hot-updata 插件,直接热更新,更新之后不需要用户重新安装程序

先看实际效果

electrin-hot-updata

引用文本 阿松大

electron的项目用 electron 自动更新,更新之后需要手动安装程序,有需求就有产出;

electron-hot-updata插件,使用本插件热更新,动态替换electron 执行文件达到热更新;

github 代码地址

使用方法

安装插件

npm i electron-hot-updata

Installing

A step by step series of examples that tell you how to get a development env running.

1. 因为electron 默认是安装在C:\Program Files\ 目录下 操作文件需要管理员权限;

// 在electron-build 的package.json 里面配置权限;

// 我的项目是基于vue 我就直接在vue.config.js 里面配置

    //因为electron 默认是安装在C:\Program Files\ 目录下 操作文件需要管理员权限;
     module.exports = {
        ...,
         "win": { 
            "requestedExecutionLevel": "highestAvailable" // highestAvailable 最高权限
        },
    }


2. 配置项目服务器地址
    // vue.config.js
    1.配置项目服务器地址 
    module.exports = {
        ...,
        publish: [{
           "provider": "generic",
           url: "http://192.168.0.191/electron/" // 更新文件服务器地址 放你后台的地址
         }],
     }
        配置之后执行electron-build 打包之后会自动生成 latest.yml 文件

    2.sasr 配置   //看你自己需求  可有可无
    module.exports = {
        ...,
        sasr: false  //  执行文件是否打包成sasr文件
     }

    sasr:false   // 打包出来的就是 html + css + js   
    sasr:true   // 打包之后就是sasr 文件

    ##
    http://192.168.0.191/electron/ 下面存放两个文件
        latest.yml      
            系统打包之后自动生成
        app.zip     
            //app.zip 分两种情况
            1.sasr:false 
                项目/dist_electron/win-unpacked/resources/  下找到app文件夹 打包成 app.zip 文件
            2.sasr:true 
                 项目/dist_electron/win-unpacked/resources/  下找到 app.sasr文件 打包成app.zip 文件  
        
3. 版本号 如果你有更好的修改项目版本号办法 直接无视这一块
    electron 版本区分默认依靠 项目>package.json>version;
    所有每次打包之后 都要修改版本号 ;
    靠版本号来区分是否需要更新
    

    在vue.cinfog.js 里面添加以下代码  
    <!-- 一下代码每次运行项目都会修改版本号 -->

    const path = require('path')
    const fs = require('fs');
    const resolve=(dir)=> {
        return path.join(__dirname, dir)
    }
    const AddZero = (time)=> {
        if (time < 10) {
            return "0" + time
        } else {
            return time
        }
    }
    let packageTxt = fs.readFileSync('./package.json', 'utf8');
    let versionData = packageTxt.split('\n');
    let packageJson = JSON.parse(packageTxt);
    let VersionArr = packageJson.version.split('.');
    let date = new Date();
    let today = date.getFullYear() + "" + AddZero((date.getMonth() + 1)) + "" + AddZero(date.getDate())
    if (today == VersionArr[1]) {
        VersionArr[2] = parseInt(VersionArr[2]) + 1
    } else {
        VersionArr[1] = date.getFullYear() + "" + AddZero((date.getMonth() + 1)) + "" + AddZero(date.getDate())
        VersionArr[2] = 1;
    }
    let versionLine = VersionArr.join('.');
    for (let i = 0; i < versionData.length; i++) {
        if (versionData[i].indexOf('"version":') != -1) {
            versionData.splice(i, 1, '  "version": "' + versionLine + '",');
            break;
        }
    }
    fs.writeFileSync('./package.json', versionData.join('\n'), 'utf8');

根目录新建 hot-updata.js

 //  hot-updata.js 里面代码
import {electronHotupdata} from "electron-hot-updata"
const electronHotupdatas= new electronHotupdata(this)
const path = require('path');
import {
    ipcMain,
    app
} from 'electron'
let mainWindow=null
export function updateAva(windows,provider,Version){
    mainWindow=windows
    // 设置地址
    electronHotupdatas.setProvider(provider)
    // 设置版本号
    electronHotupdatas.setNewVersion(Version)

    //  ***  一般情况下不介意修改地址
    // electron 默认安装在 C:\Program Files\ 目录下   如果热更新地址不对  electronHotupdatas.setBaseUrl 动态修改地址    
    // electronHotupdatas.setBaseUrl(baseUrl)


    // 监听获取版本失败
    electronHotupdatas.on("get-version-failed",(message)=>{
        sendHotUpdateMessage({
            cmd: 'get-version-failed',
            message: message
        })
    })

    // 监听没有新版本
    electronHotupdatas.on("no-new-version",(message)=>{
        sendHotUpdateMessage({
            cmd: 'no-new-version',
            message: message
        })
    })

    // 监听有新版本  可更新
    electronHotupdatas.on("new-version-available",(message)=>{
        sendHotUpdateMessage({
            cmd: 'new-version-available',
            message: message
        })
    })

     // 监听获取版本失败 更新失败
     electronHotupdatas.on("downloaded-failed",(message)=>{
        sendHotUpdateMessage({
            cmd: 'downloaded-failed',
            message: message
        })
     })

    // 监听更新完成
    electronHotupdatas.on("complete-downloaded",(message)=>{
        sendHotUpdateMessage({
            cmd: 'complete-downloaded',
            message: message
        })
    })

    // 跟新完成立即重启程序
    ipcMain.on("relaunch",()=>{
        app.relaunch()
        app.exit()
    })

    // 触发更新事件
    ipcMain.on("start-updata",(message)=>{
       
        electronHotupdatas.startUpdata()
    })

    // 获取跟新进度
    electronHotupdatas.on("downloaded-progressNumber",(message)=>{
        sendHotUpdateMessage({
            cmd: 'downloaded-progressNumber',
            message: message
        })
    })


    // 触发获取新版本事件
    ipcMain.on("startLoop",(message)=>{
       
        electronHotupdatas.startLoop()
    })

}


//给渲染进程发送消息
function sendHotUpdateMessage(text) {
    mainWindow.webContents.send('hotMessage', text)
}

在background.js 调用 hot-updata.js


import { app, protocol, BrowserWindow,Menu } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'

// 自动更新
import {updateAva} from './hot-updata.js' ;

const isDevelopment = process.env.NODE_ENV !== 'production'

// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
  { scheme: 'app', privileges: { secure: true, standard: true } }
])

async function createWindow() {
  // Create the browser window.
  const win = new BrowserWindow({
    width: 900,
    height: 800,
    frame:true,
    webPreferences: {

      // Use pluginOptions.nodeIntegration, leave this alone
      // See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
      // nodeIntegration: (process.env
      //     .ELECTRON_NODE_INTEGRATION as unknown) as boolean,
      // contextIsolation: !(process.env
      //     .ELECTRON_NODE_INTEGRATION as unknown) as boolean,
      nodeIntegration:true,
      contextIsolation:false,
      webSecurity: false,
      // preload:'./src/preload.js' ,
      preload:__dirname+'\\preload.js' ,    //  必须要引入 preload.js  不然获取不到electron 
    }
  })
  Menu.setApplicationMenu(null)

  if (process.env.WEBPACK_DEV_SERVER_URL) {
    // Load the url of the dev server if in development mode
    await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string)
    if (!process.env.IS_TEST) win.webContents.openDevTools()
  } else {
   
    createProtocol('app')
    // Load the index.html when not in development
    win.loadURL('app://./index.html')
    win.webContents.openDevTools()
    // win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string)
  }
   //  http://192.168.0.191/electron/ 是我本地的测试地址   换成你服务器的地址
  updateAva(win, 'http://192.168.0.191/electron/',app.getVersion());
}

// Quit when all windows are closed.
app.on('window-all-closed', () => {
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (BrowserWindow.getAllWindows().length === 0) createWindow()
})

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
  // if (isDevelopment && !process.env.IS_TEST) {
  //   // Install Vue Devtools
  //   try {
  //     await installExtension(VUEJS_DEVTOOLS)
  //   } catch (e:any) {
  //     console.error('Vue Devtools failed to install:', e.toString())
  //   }
  // }
  createWindow()
})

// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
  if (process.platform === 'win32') {
    process.on('message', (data) => {
      if (data === 'graceful-exit') {
        app.quit()
      }
    })
  } else {
    process.on('SIGTERM', () => {
      app.quit()
    })
  }
}

在app.vue 里面调用electron 获取最新版本

// ts 
const ipcRenderer = (window as any).ipcRenderer
// js

const ipcRenderer =window.ipcRenderer

//  window.ipcRenderer === undefined  说明没有引入 preload.js
created() {  
      let _this = this;
      if (ipcRenderer) {
        window.setInterval(() => {
          console.log("循环开始检查更新....")
          ipcRenderer.send("startLoop");
        }, 10000);

        ipcRenderer.on("hotMessage", (event: any, arg: any) => {
          console.log(arg);
          if (arg.cmd == 'new-version-available') {
            _this.modalVisible = true
          } else if (arg.cmd == 'downloaded-failed') {
            console.log('更新失败')
          } else if (arg.cmd == 'downloaded-progressNumber') {
            console.log('获取跟新进度' + arg.message)
            _this.progress = arg.message
          } else if (arg.cmd == 'complete-downloaded') {
            console.log('更新完成')
            // 更新完成 立即 重启程序
            ipcRenderer.send("relaunch");
          }
        });
      }
    }

preload.js

const electron = require('electron');
window.ipcRenderer = require('electron').ipcRenderer;
window.remote = require('electron').remote;
### Electron 应用中实现更新的方法 #### 使用 `autoUpdater` API 进行自动更新 为了在 Electron 中实现更新功能,官方提供了 `autoUpdater` 模块来简化这一流程。通过配置服务器地址并设置相应的事件处理逻辑,可以轻松完成应用的在线升级。 对于基于 Electron 构建的应用程序而言,在主进程中引入必要的模块之后,可以通过如下方式构建一个基本的更新检查机制: ```javascript const { app, autoUpdater } = require('electron'); const serverURL = 'http://your-update-server.com'; function checkForUpdates() { autoUpdater.setFeedURL(`${serverURL}/update/${process.platform}/${app.getVersion()}`); autoUpdater.on('update-available', () => { console.log("发现版本"); }); autoUpdater.on('update-not-available', () => { console.log("当前已是最版"); }); autoUpdater.on('error', (error) => { console.error(`更新过程中发生错误: ${error.message}`); }); autoUpdater.checkForUpdates(); } ``` 此段代码展示了如何利用 `autoUpdater` 来检测是否有的应用程序版本可供下载[^2]。 #### 主动触发与被动等待相结合的方式 除了定时或特定条件下主动发起更新请求外,还可以允许用户自行决定何时执行更新操作。这通常涉及到向页面注入通信接口以便接收来自前端界面的操作指令,并由后台服务负责实际的数据传输工作。 当接收到渲染进程发送过来的消息时,主进程可以根据预设策略调用之前定义好的更新函数来进行下一步动作。由于该过程是在主进程中完成的,因此能够确保整个更新流程的安全性和稳定性[^1]。 #### 安卓 APP 的 Vue 更对比 值得注意的是,虽然 Android 平台上的原生应用以及采用 Vue.js 开发的混合移动应用也支持类似的动态加载特性——即所谓的“补丁”,但是它们的工作原理和技术栈存在显著差异。例如,在 Android 上可能涉及 APK 文件结构解析或是 Webview 内容刷等问题;而针对 Electron 则更多关注于桌面环境下的文件系统访问权限控制等方面[^3]。 综上所述,Electron 提供了一套相对成熟稳定的工具链用于解决开发者们关心的各种场景需求,无论是频繁迭代期间快速验证想法还是正式发布后的维护管理都能得到良好支撑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值