该文章仅记录热更新实现,全量更新请参考官网:传送门
1.原理分析
1.1 热更新需要替换内容
electron-builder打包时,会按照package.json中build
字段的配置进行打包,其中打包的目录为files
字段,所以其实我们每次更新只需要将该部分的内容替换,不需要将整个包更新,可以减少150m左右的更新大小,同时node_modules
中的文件一般情况下也是不需要更新的,整个根据项目大小,又可以减少一波大小
1.2 包内容解析
我们files
字段中的文件最后会被electron-builder打包到一个名为app.asar
的文件中,这个app.asar
中包含了所有的内容,但其实我们仅需要将node_modules
和主进程文件(main.js)
打包进app.asar
,剩余内容放到其他地方,便于热更新替换
2.具体实现
2.1 package.json修改
第一步:将我们不需要打包进app.asar
的文件从里面拿出来
"asar": true,
"asarUnpack": [
"dist/electron/**/*",
"!dist/electron/node_modules",
"!dist/electron/main.js"
],
此处将asar直接设置成false也可以,但是这样不太安全,此处我们将需要的文件全部放入,并排除node_modules
和node_modules
第二步:增加一个热更新版本号,用于判断时候需要热更新
"hotVersion": "0.0.1"
第二步:在应用启动时判断是否需要进行热更新
这块代码不在此处贴了,我是通过接口实现,将本地的hotVersion传给后端,然后后端拿去判断后告诉我是否需要热更新
第三步:下载更新包,并且解压到需要的目录然后重启
当后端告诉我需要进行热更新时,我们就去将更新包下载下来
HotUpdate.js文件:
const { app } = require('electron')
const path = require('path')
const fs = require('fs')
const os = require('os')
const http = require('http')
const AdmZip = require('adm-zip')
const downLoadZip = () =>
new Promise((resolve, reject) => {
const tmpDir = os.tmpdir() // 电脑的缓存目录
const filename = path.resolve(tmpDir, `update-${new Date().getTime()}.zip`)
const file = fs.createWriteStream(filename)
http.get('更改成自己的下载地址', res => {
res.pipe(file)
res.on('end', () => {
resolve(filename)
})
res.on('error', err => {
reject(err)
})
})
})
const hotUpdate = () => {
const appPath = app.getAppPath() // 该方法可以获取程序app.asar的目录
const replacePath = path.resolve(appPath, '../app.asar.unpacked/dist/electron') // 通过path.resolve组装成我们需要的路径
downLoadZip().then(filename => {
const zip = new AdmZip(filename) // 该工具可以压缩和解压,具体请自己去查看
zip.extractAllTo(replacePath, true)
app.relaunch()
app.exit()
})
}
module.exports = {
hotUpdate
}
main.js文件中
import { hotUpdate } from './HotUpdate'
ipcMain.on('hotUpdate', () => {
hotUpdate()
})
该方法需要绑定到主进程,不能在渲染进程中使用,会造成程序崩毁,当需要更新时,直接在渲染进程中调用主进程的该方法即可
3.打包出自己需要的热更新文件
const AdmZip = require('adm-zip')
function createZip() {
const zip = new AdmZip()
zip.addLocalFolder('dist/electron', '', fileName => {
// 文件夹需要这样判断,因为如果文件夹内部有文件,fileName会是一个完整路径
if (fileName.indexOf('node_modules') > -1) {
return false
}
// 文件放在该处
return !['main.js'].includes(fileName)
})
zip.writeZip('dist/update.zip') // 输出路径
}
4.将打包出的文件放到下载地址
上一步和这一步其实可以和打包发布平台放一起,让运维写一个脚本即可自动完成