【踩坑记录】Electron+vue实现热更新

引出问题

目前需要实现electron热更新问题,需要静默更新

实现思路

  1. 新增版本文件
  2. 远程获取版本和下载地址,进行对比
  3. 发现需要更新后,进行update.asar下载
  4. 将update移动到res目录下与app.asar共存
  5. 开启子进程,开启exe,exe内容如下
  6. 写一个bat转换为exe,内容为将进程关闭,删除原有asar,将update.asar重命名为app.asar,重启主线程
  7. 完成更新

创建脚本文件

@echo off
taskkill /f /im %1
timeout /T 1 /NOBREAK
del /f /q /a %2\app.asar
ren %2\update.asar app.asar
start ""  %3
taskkill /f /im startUpdate.exe

使用工具:Bat To Exe Converter 将bat打包为exe文件

生成exe后可以手动先试一下
.\resources\upgured\startUpdate.exe windowspay .\resources windowspay.exe

核心代码如下

downloadFile.js(下载资源文件)
import axios from "axios";
import fs from "fs";

export default (url, downloadFilePath) => {
  console.log("保存文件位置:" + downloadFilePath);
  return new Promise(async (resolve, reject) => {

    try {
      let { data } = await axios({
        url: url,
        responseType: 'arraybuffer',
      })
      await fs.promises.writeFile(downloadFilePath, data, 'binary');
      resolve()
    } catch (error) {
      reject(error)
    }
  })
}
sudoPrompt.js(执行exe脚本)
const { exec } = require('child_process');

export default (param) => {
  return new Promise((resolve, reject) => {
    let bat = param.join(" ");
    console.log(bat);
    exec(bat, (err, stdout, stderr) => {
      if (err) {
        console.log(err);
        reject(err)
        return;
      }
      console.log(stdout);
      console.log(stderr);
    });
    resolve();
  })
}

upgruedScheduleEvent.js(核心代码)
import schedule from 'node-schedule'
import api from '../../api'
import hos from '../../../../store/hos'
import axios from "axios";
import fs from "fs";
import versionContrast from "./versionContrast";
import downloadFile from "./downloadFile";
import sudoPrompt from "./sudoPrompt";
import { app, ipcMain } from 'electron';

let cron = '*/1 * * * *';
let resources = process.resourcesPath
let versionFilePath = resources + '\\upgured\\version';
let execFile = resources + '\\upgured\\startUpdate.exe';
let appExeFileload = app.getPath('exe');
let processName = process.env.VUE_APP_PRODUCTNAME + '.exe'
let downloadNewFileName = resources + '\\update.asar'
let upgruedUrl = ''
let remoteVersion = ''
let hasDownloadUpgruedFile = false


export default (sendMsg2RendererByHotUpgrued) => {
    bootstrapUpgrued(api.upgrued + "/" + hos.mac, sendMsg2RendererByHotUpgrued);
}

function bootstrapUpgrued(url, sendMsg2RendererByHotUpgrued) {
    schedule.scheduleJob(cron, function () {
        axios.get(url).then(res => {
            remoteVersion = res.data.data.version;
            upgruedUrl = res.data.data.upgruedUrl;
            let oldVersion = fs.readFileSync(versionFilePath).toString();
            console.log(remoteVersion, upgruedUrl, oldVersion);
            let isDownload = versionContrast(remoteVersion, oldVersion);
            if (isDownload == -1 && !hasDownloadUpgruedFile) {
                console.log("开始进行更新操作");
                sendMsg2RendererByHotUpgrued()
            } else {
                console.log("无需更新操作");
            }
        }).catch(err => {
            console.log(err)
        })
    });
}

ipcMain.handle("sendMsg2MainByHotUpgrued", async (event, someArgument) => {
    const result = await downloadUpgruedFileAndUpdate(someArgument)
    return result
})

let downloadUpgruedFileAndUpdate = (someArgument) => {
    hasDownloadUpgruedFile = true;
    downloadFile(upgruedUrl, downloadNewFileName).then(() => {
        weiteVersion(remoteVersion).then(async () => {
            //.\resources\upgured\startUpdate.exe windowspay .\resources windowspay.exe
            await sudoPrompt(
                [execFile, processName, resources, appExeFileload]
            )
        })
    }).catch(err => {
        console.log("下载有误");
        console.log(err);
    })
}

let weiteVersion = (remoteVersion) => {
    return new Promise(async (resolve, reject) => {
        fs.writeFile(versionFilePath, remoteVersion, (err) => {
            if (err) {
                console.log('读取文件失败', err.message)
                reject(err.message)
            }
            console.log('版本文件更新成功!')
            resolve()
        })
    })

}



versionContrast.js(对比版本)
export default (v1, v2) => {
    //补位0,或者使用其它字符
    const ZERO_STR = '000000000000000000000000000000000000000000';
    if (v1 === v2) {
        return 0;
    }
    let len1 = v1 ? v1.length : 0;
    let len2 = v2 ? v2.length : 0;
    if (len1 === 0 && len2 === 0) {
        return 0;
    }
    if (len1 === 0) {
        return 1;
    }
    if (len2 === 0) {
        return -1;
    }
    const arr1 = v1.split('.');
    const arr2 = v2.split('.');
    const length = Math.min(arr1.length, arr2.length);
    for (let i = 0; i < length; i++) {
        let a = arr1[i];
        let b = arr2[i];
        if (a.length < b.length) {
            a = ZERO_STR.substr(0, b.length - a.length) + a;
        } else if (a.length > b.length) {
            b = ZERO_STR.substr(0, a.length - b.length) + b;
        }
        if (a < b) {
            return 1;
        } else if (a > b) {
            return -1;
        }
    }
    if (arr1.length < arr2.length) {
        return 1;
    } else if (arr1.length > arr2.length) {
        return -1;
    }
    return 0;
}

实现更新后弹出遮罩层

实现思路

  1. 编写遮罩层代码(app.vue)
  2. 主线程主动使用ipc通知渲染线程渲染代码
main.js(主线程与渲染线程交互)
import { createApp } from 'vue'
import ElementPlus from 'element-plus';
import App from './App.vue'
import router from "../router/routes.js"
import i18n from './i18n/index.js';
import hos from '../store/hos.js'
import card from '../store/card.js'
import { ipcRenderer } from 'electron'
// import "element-plus/dist/index.css"; // 引入组件样式

//Vue.prototype.$api = process.env.NODE_ENV == 'development' ? "/invoice" : "127.0.0.1:8080/tool";

const app1 = createApp(App)
app1.use(i18n);
app1.use(router);
app1.use(ElementPlus);
app1.config.globalProperties.$hos = hos;
app1.config.globalProperties.$card = card;
app1.mount('#app');

//重点看这里
ipcRenderer.on('sendMsg2RendererByHotUpgrued', (event, param1, param2) => {
    console.log("sendMsg2RendererByHotUpgrued", "监听开始");
    hotUpgruedHandle()
    ipcRenderer.invoke('sendMsg2MainByHotUpgrued', 'start download')
})
App.vue(弹出遮罩层)
<template>
  <div id="app">
    <div :id="hotUpgrued?'PageLoadingEffect':''">
      <div :class="hotUpgrued?'loading':''"></div>
      <div :class="hotUpgrued?'loading-content':''">{{hotUpgrued?'更新中,请勿触碰':''}}</div>
    </div>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'app',
  data(){
    return{
      hotUpgrued:false
    }
  },
  mounted() {
    window.hotUpgruedHandle = this.hotUpgruedHandle;
  },
  methods: {
    hotUpgruedHandle() {
      this.hotUpgrued = true;
    }
  }
}
</script>
<style scoped>
#PageLoadingEffect {
  position: absolute;
  left: 0px;
  top: 0px;
  width: 100%;
  height: 100%;
  background-color: rgba(112, 112, 112, 0.7);
  z-index: 99999;
}

.loading {
  margin-top: 900px;
  margin-left: 535px;
  display: block;
  position: relative;
  width: 26px;
  height: 30px;

  animation: rectangle infinite 1s ease-in-out -0.2s;

  background-color: #000;
}

.loading:before,
.loading:after {
  position: absolute;
  width: 26px;
  height: 30px;
  content: "";
  background-color: #000;
}

.loading:before {
  left: -40px;
  animation: rectangle infinite 1s ease-in-out -0.4s;
}

.loading:after {
  right: -40px;
  animation: rectangle infinite 1s ease-in-out;
}

@keyframes rectangle {
  0%,
  80%,
  100% {
    height: 40px;
    box-shadow: 0 0 #000;
  }

  40% {
    height: 50px;
    box-shadow: 0 -20px #000;
  }
}

.loading-content{
  margin: 50px 380px;
  font-size: 40px;
  font-weight: 700;
}
</style>

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要在 Electron + Vue实现截图,你可以使用 Electron 中的 desktopCapturer 模块来获取屏幕截图。以下是一些步骤: 1. 在你的 Vue 组件中引入 Electron 模块: ```javascript const { desktopCapturer } = require('electron') ``` 2. 创建一个函数来获取屏幕截图。这个函数可以使用 desktopCapturer 模块来获取桌面上的媒体源并将其转换为可用的图像。在这个函数中,你可以使用 Canvas API 或其他图像处理库来对图像进行操作。 ```javascript async function captureScreen() { const sources = await desktopCapturer.getSources({ types: ['screen'] }) const source = sources[0] const stream = await navigator.mediaDevices.getUserMedia({ audio: false, video: { mandatory: { chromeMediaSource: 'desktop', chromeMediaSourceId: source.id, minWidth: 1280, maxWidth: 1280, minHeight: 720, maxHeight: 720 } } }) const videoTrack = stream.getVideoTracks()[0] const imageCapture = new ImageCapture(videoTrack) const bitmap = await imageCapture.grabFrame() // 在这里对 bitmap 进行处理 } ``` 3. 在你的 Vue 组件中创建一个函数来触发屏幕截图函数,并将截图显示在页面上。你可以使用 canvas 元素来显示图像。 ```javascript methods: { async takeScreenshot() { const canvas = this.$refs.canvas const context = canvas.getContext('2d') context.clearRect(0, 0, canvas.width, canvas.height) const bitmap = await captureScreen() canvas.width = bitmap.width canvas.height = bitmap.height context.drawImage(bitmap, 0, 0) } } ``` 4. 在你的 Vue 组件中添加一个 canvas 元素来显示截图: ```html <canvas ref="canvas"></canvas> ``` 这样就可以在 Electron + Vue实现截图了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alaia.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值