携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天
背景介绍
公司接到需求,需要使用开发一个桌面程序用于客户自助机使用,因为没有人会搞桌面端开发,身为啥都搞的前端程序员,身披战袍出战
书接上回,经过一天的调试,终于完成了这个新奇需求的开发,当我又准备去找产品炫耀的时候,产品又黑着脸,"我们现在更新每次要客服重新下载我们的安装包,客户觉得麻烦,我希望可以做热更新,😱好家伙,来源不断的需求,热更新是吧,难不倒我的,经我缜密的调研,发现一个库electron-updater
,这个库需要配合electron的打包一起使用,那我们就从打包配置入手误会(一)打包配置
打包配置修改
...,
"publish": {
// 开发者
"provider": "generic",
// 对应包的地址
"url": "xxx",
"channel": "latest"
},
...
我们在package.json
文件中修改打包配置,添加最重要的publish
属性,该属性最重要的字段是url
字段,该字段是electron检测更新包的地址,添加好对应属性后,开始我们的更新方法编写
如何检测更新
npm install electron-updater
先下载需要使用的工具库,然后新增一个update.js
,你们爱叫啥就建啥
const { autoUpdater } = require('electron-updater')
const { ipcMain } = require('electron')
let mainWindow = null
function sendUpdateMessage (text) {
mainWindow.webContents.send('message', text)
}
module.exports = function updateHandle(window, updateURL) {
mainWindow = window
const message = {
error: '检查更新出错',
checking: '正在检查更新…',
updateAva: '正在更新',
updateNotAva: '已经是最新版本',
downloadProgress: '正在下载...'
}
// 设置是否自动下载,默认是true,当点击检测到新版本时,会自动下载安装包,所以设置为false
autoUpdater.autoDownload = false
// 设置地址
autoUpdater.setFeedURL(updateURL)
// 报错反馈
autoUpdater.on('error', function (e) {
sendUpdateMessage({ cmd: 'error', message: message.error, e })
})
// 检查更新反馈
autoUpdater.on('checking-for-update', function (info) {
sendUpdateMessage({ cmd: 'checking-for-update', message: message.checking, info })
})
autoUpdater.on('update-available', function (info) {
sendUpdateMessage({ cmd: 'update-available', message: message.updateAva, info })
})
autoUpdater.on('update-not-available', function (info) {
sendUpdateMessage({ cmd: 'update-not-available', message: message.updateNotAva, info: info })
})
// 更新下载进度事件
autoUpdater.on('download-progress', function (progressObj) {
sendUpdateMessage({ cmd: 'downloadProgress', message: message.downloadProgress, progressObj })
})
autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
autoUpdater.quitAndInstall()
mainWindow.destroy()
})
// 主线程检查更新事件
ipcMain.on('checkForUpdate', () => {
sendUpdateMessage({ cmd: '执行自动更新检查', message: '执行自动更新检查' })
autoUpdater.checkForUpdates()
})
// 主线程开始更新事件
ipcMain.on('downloadUpdate', () => {
log.warn('执行下载')
autoUpdater.downloadUpdate()
})
这个文件是封装了一个更新事件的回调,并通过接受主线程的消息来进行更新,这样可以有效的控制程序的更新而不是自动更新,然后主线程引入文件方法
const upgradeFn = require('./update')
app.whenReady().then(() => {
...,
// 第一个参数传入 electron主体应用对象
// 第二个参数就是包对应的服务器地址和packag.json里面一致
upgradeFn(win, xxx)
...
});
做完上述准备后,我们就可以进行调试,首先进行首次打包,打包ing>>>>>>,叮!,打包完成,会输入以下文件
因为公司的东西,需要马赛克,生成一个对应的安装包和一个latest.yml
文件,latest.yml
是重中之重,electron
之后会通过该文件进行对比检测是否有更新程序
启动一个测试服务器
作为一个前端程序员,应该要会自己启服务器,只有拥有充分的后端知识,才可以和别人battle🐕,这里我直接上代码了
const express = require('express')
const app = express()
const cors = require('cors')
const path = require('path')
app.use('/static', express.static(path.join(__dirname, 'public')))
app.use(cors())
app.listen(8001)
console.log('server start at 8001')
app.get('/', (req, res) => {
res.send('welcome JueJin')
})
启动一个服务器,把刚刚打包生成的两个文件放在服务器public
文件下,服务器目录结构
更改electron版本,再次打包
现在我们需要做的就是,改一点代码,然后再重新生成一份安装包,不过这里需要注意,我的项目electron
版本是和package.json
文件里的版本是对应的,需要修改一下,两个不同版本的文件才有对比的意义,打包中>>>>>>,叮~完成。
然后我们打开打包完成后的程序,通过页面通知主线程的checkForUpdate
的命令,electron就会显示检测的结果了,这里我忘了截图,就不截了🐕
我踩过的坑,你们继续踩,electron的通信问题
electron
中,主线程和渲染线程之间的通信是通过自带的ipcMain
方法进行通信,具体使用方法可以看文档,其实就是简单的发布订阅,不过这里有个巨坑,就是渲染线程想要调用ipcMain
方法时,需要用electron
作为桥梁传递该对象给到渲染进程(vue页面)
,这里就需要使用electron
的预加载功能
新建preload.js
文件
const { contextBridge, ipcRenderer } = require('electron/renderer')
// 注册ipc
contextBridge.exposeInMainWorld('electron', {
ipcRenderer
})
上述代码,通过electron
自带的contextBridge
方法,传递对象,然后在主线程注册preload.js
,这里很简单就不复制代码了,但是当你使用contextBridge
传递对象的时候,在vue页面接受到的ipcRenderer
对象之后监听方法,缺没有发布方法,所以无法通过页面去通知electron
进行任何操作,包括更新。一杯茶一包烟,一个bug修一天,就我花了整整一天去研究这个bug的时候,在github上给我找到了解决方法
大佬的意思简单就是说,如果直接通过ipcRenderer
对象渲染进程就可以控制主线程的话,真的太不安全拉,所以我们取消了它发布的权限,如果你们还是想这么做,可以通过桥梁,给渲染进程传递对应的工厂函数哔哩吧啦一大堆,不过还是找到解决该bug的方法
// 注册ipc
contextBridge.exposeInMainWorld('electron', {
ipcRenderer: {
...ipcRenderer,
onMessage: (handler) => ipcRenderer.on('message', (event, ...args) => handler(...args))
}
})
将注册方法改成这样,vue页面可以通过ipcRenderer
对象的onMessage
事件进行发布操作,完美完美,这样子就可以通过页面通知electron
进行更新拉
结尾
当我向产品再次炫耀我做完的功能后,产品这次直接五体投地,世界终于恢复平静的摸鱼时间,暂时没有新的需求了
关于作者
一个工作三年,摆烂躺平的前端攻城狮~~~🦁