最近创建了一个electron-vue项目,用到了热更新,先看效果图:
话不多说,直接上代码。main目录下创建update.js
代码如下:
// 安装包helatest.yml所在服务器地址
const uploadUrl = 'http://127.0.0.1:8080/pc/'
const { autoUpdater } = require('electron-updater')
const { ipcMain } = require('electron') // ipcMain 主线程
const config = require("../../package.json")
const isDevelopment = process.env.NODE_ENV !== "production";
// 检测更新,在你想要检查更新的时候执行,renderer事件触发后的操作自行编写
export const updateHandle = (mainWindow) => {
let message = {
error: { status: -1, msg: '检测更新查询异常' },
checking: { status: 0, msg: '正在检查更新...' },
updateAva: { status: 1, msg: '检测到新版本' },
updateNotAva: { status: 2, msg: '您现在使用的版本为最新版本,无需更新!' },
}
if (isDevelopment && !process.env.IS_TEST) { // 调试环境
autoUpdater.currentVersion = config.version;
}
autoUpdater.setFeedURL(uploadUrl)
//在下载之前将autoUpdater的autoDownload属性设置成false,通过渲染进程触发主进程事件来实现这一设置
autoUpdater.autoDownload = false;
// 检测更新查询异常
autoUpdater.on('error', function (error) {
sendUpdateMessage(mainWindow, message.error)
})
// 当开始检查更新的时候触发
autoUpdater.on('checking-for-update', function () {
sendUpdateMessage(mainWindow, message.checking)
})
// 当发现有可用更新的时候触发,更新包下载会自动开始
autoUpdater.on('update-available', function (info) {
// 主进程向renderer进程发送是否确认更新
mainWindow.webContents.send('isUpdateNow', info)
sendUpdateMessage(mainWindow, message.updateAva)
})
// 当发现版本为最新版本触发
autoUpdater.on('update-not-available', function (info) {
sendUpdateMessage(mainWindow, message.updateNotAva)
})
// 更新下载进度事件
autoUpdater.on('download-progress', function (progressObj) {
mainWindow.webContents.send('downloadProgress', progressObj)
})
// 包下载成功时触发
autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
autoUpdater.quitAndInstall() // 包下载完成后,重启当前的应用并且安装更新
})
// 收到renderer进程确认更新
ipcMain.on('updateNow', (e, arg) => {
console.log('开始更新... ...')
autoUpdater.downloadUpdate();
})
ipcMain.on('checkForUpdate', () => {
// 收到renderer进程的检查通知后,开始检查更新
autoUpdater.checkForUpdates()
})
}
// 通过main进程发送事件给renderer进程,提示更新信息
function sendUpdateMessage(mainWindow, text) {
mainWindow.webContents.send('message', text)
}
在主进程中引入:
import { updateHandle } from './update' //引入
在主进程中引用:
为了显示更新进度条,友好的交互,封装了个update组件:
显示效果如图(可以根据需要在组件中设置更新是否可关闭,是否强制更新...)
组件代码:
<template>
<transition name="fade">
<div v-if="show">
<div class="modal"></div>
<div class="update">
<div class="header">
<h2>发现新版本</h2>
<!-- 可关闭的更新
<i class="close el-icon-close" @click="close"></i></div> -->
<!-- 不可以关闭的更新 -->
<i class="close"></i></div>
<div class="body">
<p>更新进度</p>
<p class="percentage">{{downloadPercent}}</p>
<div class="progress">
<div class="length" :style="'width:'+downloadPercent"></div>
</div>
</div>
</div>
</div>
</transition>
</template>
<script>
export default {
name: "update",
data() {
return {
}
},
methods: {
close() {
this.$emit('update:show', false)
}
},
props: {
show: {
type: Boolean,
required: true,
default: false
},
downloadPercent:{
type: String,
required: true,
default: '0%',
}
}
}
</script>
<style scoped>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter, .fade-leave-to{
opacity: 0;
}
.modal {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0.4;
background: #000;
}
.update {
width: 400px;
height: 180px;
background-color: #FFFFFF;
border-radius: 10px;
border: 1px solid #CCC;
position: absolute;
top: 40%;
margin-top: -90px;
left: 50%;
margin-left: -200px;
box-shadow: #FFFFFF 0 0 10px;
}
.update .header i.close {
display: inline-block;
position: absolute;
top: 11px;
right: 12px;
width: 20px;
height: 20px;
background-size: 100%;
cursor: pointer;
}
.update .header {
border-bottom: 1px solid #ccc;
height: 50px;
line-height: 40px;
}
.update .header h2 {
text-align: center;
font-size: 20px;
margin-top: 5px;
}
.update .body {
padding-top: 0px;
text-align: center;
}
.update .body .percentage {
margin-top: 20px;
}
.update .body .progress {
width: 350px;
height: 30px;
border: 1px solid #CCCCCC;
border-radius: 8px;
margin: 10px auto;
}
.update .body .progress .length {
background-color: #E4393c;
border-radius: 8px;
height: 30px;
}
</style>
组件的调用(我是在App.vue中引入的,程序启动即加载更新,可根据实际需求设置):
在template中的div中引入即可
<update :show.sync="updateShow" :percent="updatePercent" :downloadPercent="updatePercent"></update>
对应的js:
<script>
const {ipcRenderer} = require('electron')
import update from './../renderer/components/frame/update'
export default {
name: 'textApp',
components:{
"update" : update
},
data () {
return {
updateShow:false,
updatePercent:'0%',
}
},
mounted:function(){
//给渲染进程添加更新监听事件
this.updateListenerRegister();
},
methods: {
updateListenerRegister(){
let that = this;
// 软件热更新相关
ipcRenderer.on("message", (event, data) => {
switch (data.status){
case -1:
that.$message.error(data.msg);
break;
// case 0:
// this.$message(data.msg);
// break;
case 1:
that.updateShow = true;
break;
}
});
ipcRenderer.on("downloadProgress", (event, progressObj) => {
// console.log("downloadProgress", progressObj);
that.updatePercent = (progressObj.percent).toFixed(2)+'%';
if (progressObj.percent >= 100) {
that.updateShow = false;
}
});
ipcRenderer.on("isUpdateNow", (event, versionInfo) => {
// 自定义选择效果,效果自行编写 tip是我在 latest.yml 中手动添加的更新内容提示
// 用的element-ui 可自行安装
// that.$confirm('检测到最新'+ versionInfo.version+'版本已上线,是否立即升级?', "温馨提示",
// {confirmButtonText: "确定", cancelButtonText: "取消"} ).then(() => { //用户点击确认
// ipcRenderer.send("updateNow");
// }).catch(() => { //用户点击取消
// that.updateShow = false;
// });
//不提示用户,强制更新
ipcRenderer.send("updateNow");
});
ipcRenderer.send("checkForUpdate");
},
},
}
</script>
需要注意package.json中的这几个依赖的版本(electron-updater3.0以下的版本会报错,要是不知道咋解决,按照本博客中的版本试一下), 在网上看到的都是降低版本,什么版本号配什么,最后就发现 只需要把对应低版本升级了就可以了,还发觉一个问题,打包的文件不能是中文,不然更新的时候路径会有问题,但是打包好后安装的应用名称中文的话可以在nsis属性中shortcutName属性配置,不然安装后默认打包的名称)
配置应用安装的属性:
特别注意点:
①本博客中我的路径是...../pc/目录下,对应的latest.yml和更新包一定要放在同一目录下,都在 .../pc/文件夹下
②打更新包的名称(即productName配置的名称)不可以为中文,否则下载路径中文变乱码找不到。
③默认打包的名称是对应着productName 配置的名称 Setup 版本号.exe,如果当把文件上传到服务器ubuntu环境,自动空格处添加了下划线导致在使用更新的使用出现下载路径404的错误。
可以 加属性 "artifactName": "${productName}_Setup_${version}.${ext}",或者手动修改安装包的名称同时对应着修改更新包的名称。