增量更新
常见的更新方案
- 通过接口判断是否需要更新,需要的话通过shell.openExternal(downloadUrl) 打开浏览器进行下载更新
- 增量更新,通过比对版本。拉取新的压缩包,再覆盖之前的renderer层来实现
思路:
- 点击增量更新按钮时renderer层通知main层进行更新
- main层比对是否需要更新,需要拉取最新的dist包
- 删除之前包,复制新的包进去
- 重启应用
需要下载的依赖
npm install axios
npm install extract-zip //用于解压更新文件的zip包
代码逻辑
一些层级不太懂的可以去翻阅之前关于electron文章,从0到1搭建electron-vite-demo
-
renderer层
src/renderer/pages/index/index.vue 点击发送给main层进行更新
<template> <h1>更新界面 1.1.2</h1> <div class="actions"> <el-button @click="checkUpdate"> 增量更新 </el-button> </div> </template> <script setup lang="ts"> import { send, on } from '@renderer/utils/ipcRenderer' function checkUpdate() { //* 检查更新方法 发送通信 send('hot-update') //通信到main层 } on('hot-update-success', (event, age) => { console.log('热更新成功',age) //todo 可添加重启方法 }) </script> -
main层 监听renderer的方法 并进行更新
src/main/services/ipcMain.ts 进行监听
//* 用于管理main层的监听工具类 import { BrowserWindow, ipcMain } from "electron"; //* 引入通信后需要使用的工具类 import updater from './hotUpdater'; //* 引入工具类 import { getMainWindowByIpcEvent, getDownloadPath, getHistoryPath } from '../utils/MainWindow' function onEvent() { //* 监听从rendener层传送过来的数据 ipcMain.handle('hot-update', (event, arg) => { updater(BrowserWindow.fromWebContents(event.sender)) }) } export function Mainfunc() { //* 监听事件 onEvent() } //* 用于管理ipc通信 export default { }src/main/services/hotUpdater.ts 更新方法
//* 增量更新 热更新 //* 引入使用的依赖 import axios from "axios" import { join, resolve } from "path" import { pipeline } from "stream" import { promisify } from "util" import { BrowserWindow, app } from 'electron'; import { gt } from 'semver' //* 用于比对版本大小 import extract from 'extract-zip' //* 用于解压zip包 import { copy, createWriteStream, emptyDir, readFile, remove } from "fs-extra"; import { createHmac } from "crypto"; //* 引入需要的配置项 // import { version } from '../../../package.json' const streamPipeline = promisify(pipeline) //* 用于存放文件 const request = axios.create() //* 用于请求 拉取文件数据 const appPath = app.getAppPath() //* 获取app安装的路径 const updatePath = resolve(appPath, '..', '..', 'update') //* 增量更新的路径 用于存放更新的zip包 const baseUrl = 'http://localhost:5500/' //* 更新文件下载路径 const version = "1.0.0" /** * 热更新方法 * @param mainWindow 请求更新的窗口 用于做页面更新使用 */ export default async (mainWindow?: BrowserWindow) => { try { // 1. 获取版本更新json 判断是否需要更新 const res = await request({ url: baseUrl + 'hot-update.json' }) //* 2. 比对版本 如果不需要更新则暂停 if(!gt(res.data.version, version)) return //* 3. 先清空放置更新文件的文件夹 await emptyDir(updatePath) //* 4. 获取更新文件的下载路径下载并存放到更新文件夹中 const updateFilePath = join(updatePath, res.data.name) await download(baseUrl + res.data.name, updateFilePath) // 5. 获取数据并比对 // const buffer = await readFile(updateFilePath) // const sha256 = hash(buffer) // console.log(sha256, '<===== sha256') // if (sha256 !== res.data.hash) throw new Error('sha256 error') // 生成临时文件 将下载下来的文件解压到临时文件夹 const appPathTemp = join(updatePath, 'temp') await extract(updateFilePath, { dir: appPathTemp }) // 删除app包 await remove(join(`${appPath}`, 'dist')); await remove(join(`${appPath}`, 'package.json')); //* 更新app包 await copy(appPathTemp, appPath) mainWindow?.webContents.send('hot-update-success', { updateFilePath, updatePath, appPath }) resolve('success') } catch (error) { console.log('下载过程中出现问题', error) } } /** * @param data 文件流 * @param type 类型,默认sha256 * @param key 密钥,用于匹配计算结果 * @returns {string} 计算结果 * @author umbrella22 * @date 2021-03-05 */ function hash(data, type = 'sha256', key = 'Sky') { const hmac = createHmac(type, key) hmac.update(data) return hmac.digest('hex') } /** * @param url 下载地址 * @param filePath 文件存放地址 * @returns {void} * @author umbrella22 * @date 2021-03-05 */ async function download(url: string, filePath: string) { const res = await request({ url, responseType: "stream" }) await streamPipeline(res.data, createWriteStream(filePath)) }测试是否成功
需要的修改的地方为hotUpdate.ts中的资源文件地址(baseUrl)
需要的文件为
- hot-update.json,用于比对版本和获取更新文件名称
- dist文件夹包:里面有package.json和之前启动编译的dist包。将其压缩成zip包后放置到资源文件夹中供下载