Vue electron自动更新 /electron手动更新
Uncaught TypeError: fs.existsSync is not a function
所有的帖子都是一样的,都不去试一下 ,这偏文章我把坑都走了一遍。
直接上代码
最主要的就是这个问题 const electron = require(‘electron’).ipcRenderer 能不用引用
这个问题解决了 后面的就简单了
这么多帖子 居然没有一个说这个问题
代码在gitee上 地址在最下面
直接引用就会报错
#Uncaught TypeError: fs.existsSync is not a function
产生问题的原因:
首先在渲染进程属于浏览器端,没有集成Node的环境,所以类似 fs 这样的Node的基础包是不可以使用。
因为没有Node环境,所以require关键词是不可以使用的。
这个 * 上找的方法
新建一个 preload.js
const electron = require('electron');
window.ipcRenderer = require('electron').ipcRenderer; // 直接吧 electron.ipcRenderer 方法挂载在windows上面
window.remote = require('electron').remote;
在main.js文件中的 webPreferen中设置预加载preload:
const win = new BrowserWindow({
width: 1920,
height: 1040,
frame:true,
webPreferences: {
nodeIntegration:true,
contextIsolation:false,
webSecurity: false,
preload:__dirname+'\\preload.js' ,
}
})
这个重点提一下preload.js 存放位置 位置真的很重要 不然就 undefined
本地的 preload.js 放在 打包之后的位置
打包之后的 preload.js 放在 public 下 和index.html 同目录
放了两个 我之前就是 只放了一个 所有 本地运行没问题 打包之后就报错了
真特么坑 居然没有一个帖子说这个问题 也有可能只有我遇见了这个问题
不然就是这样 找不到
下面开始 安装 electron-updater
// 推荐yarn
yarn add electron-updater --save-dev
// *不推荐 npm 反正我是一次都没装成功过
**npm i electron-updater --save-dev
这个必须有 不然打包不会生成 latest.yml 文件
每次更新把 yml 和 exe 丢给后端就行了
//这个必须有 不然打包不会生成 latest.yml 文件
publish:[
{
"provider":"generic",
url: "http://192.168.0.191/electron/" // 更新文件服务器地址
}
],
新建updata.js
import {
autoUpdater
} from 'electron-updater'
import {
ipcMain
} from 'electron'
let mainWindow = null;
autoUpdater.autoDownload = false;
let canQuit = false;
export function updateHandle(window, feedUrl) {
mainWindow = window;
let message = {
error: '检查更新出错',
checking: '正在检查更新……',
updateAva: '检测到新版本,正在下载……',
updateNotAva: '现在使用的就是最新版本,不用更新',
};
//设置更新包的地址
autoUpdater.setFeedURL(feedUrl);
//监听升级失败事件
autoUpdater.on('error', function (error) {
sendUpdateMessage({
cmd: 'error',
message: error
})
});
//监听开始检测更新事件
autoUpdater.on('checking-for-update', function (message) {
sendUpdateMessage({
cmd: 'checking-for-update',
message: message
})
});
//监听发现可用更新事件
autoUpdater.on('update-available', function (message) {
sendUpdateMessage({
cmd: 'update-available',
message: message
})
});
//监听没有可用更新事件
autoUpdater.on('update-not-available', function (message) {
sendUpdateMessage({
cmd: 'update-not-available',
message: message
})
});
// 更新下载进度事件
autoUpdater.on('download-progress', function (progressObj) {
sendUpdateMessage({
cmd: 'download-progress',
message: progressObj
})
});
autoUpdater.on('close', (event) => {
if (!canQuit) {
mainWindow.hide();
mainWindow.setSkipTaskbar(true);
event.preventDefault();
}
});
//监听下载完成事件
autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl) {
sendUpdateMessage({
cmd: 'update-downloaded',
message: {
releaseNotes,
releaseName,
releaseDate,
updateUrl
}
})
//退出并安装更新包
if (process.platform !== 'darwin') {
canQuit = true;
autoUpdater.quitAndInstall();
}
// autoUpdater.quitAndInstall();
});
//接收渲染进程消息,开始检查更新
ipcMain.on("checkForUpdate", (e, arg) => {
//执行自动更新检查
// sendUpdateMessage({cmd:'checkForUpdate',message:arg})
if(arg){
autoUpdater.autoDownload = true;
}
autoUpdater.checkForUpdates();
})
}
//给渲染进程发送消息
function sendUpdateMessage(text) {
mainWindow.webContents.send('message', text)
}
backgrounnd.ts 我用的ts 这个和你们main.js 是一样
import { app, protocol, BrowserWindow,Menu } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
// import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
// 自动更新 引入上面创建的 updater.js
import {updateHandle} from './updater.js' ;
const isDevelopment = process.env.NODE_ENV !== 'production'
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
{ scheme: 'app', privileges: { secure: true, standard: true } }
])
async function createWindow() {
// Create the browser window.
const win = new BrowserWindow({
width: 1920,
height: 1040,
frame:true,
webPreferences: {
// Use pluginOptions.nodeIntegration, leave this alone
// See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
// nodeIntegration: (process.env
// .ELECTRON_NODE_INTEGRATION as unknown) as boolean,
// contextIsolation: !(process.env
// .ELECTRON_NODE_INTEGRATION as unknown) as boolean,
nodeIntegration:true,
contextIsolation:false,
webSecurity: false,
// preload:'./src/preload.js' ,
preload:__dirname+'\\preload.js' , 这里是重点 // 这里引入 preload.js
}
})
Menu.setApplicationMenu(null)
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string)
if (!process.env.IS_TEST) win.webContents.openDevTools()
} else {
createProtocol('app')
// Load the index.html when not in development
win.loadURL('app://./index.html')
win.webContents.openDevTools()
// win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string)
}
updateHandle(win, 'http://192.168.0.191/electron/');
}
// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
// if (isDevelopment && !process.env.IS_TEST) {
// // Install Vue Devtools
// try {
// await installExtension(VUEJS_DEVTOOLS)
// } catch (e) {
// console.error('Vue Devtools failed to install:', e.toString())
// }
// }
createWindow()
})
// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
if (process.platform === 'win32') {
process.on('message', (data) => {
if (data === 'graceful-exit') {
app.quit()
}
})
} else {
process.on('SIGTERM', () => {
app.quit()
})
}
}
如果 updater.js 引用错误
在同目录 把updater.js copy 一份 吧名字改成 updater.ts 就好了
app.vue 设置更新页面
这个页面包含手动更新 和自动更新
<template>
<div id="app">
<a-config-provider :locale="zh_CN">
<router-view />
</a-config-provider>
<a-modal v-model="modalVisible" width="300PX" :footer="null" :closable="false" :keyboard="false"
:maskClosable="false" wrapClassName="supplement-detail-modal" :title="isUpdatte?'检查到新版本...':'系统正在更新...'">
<div style="width:120px;margin:30px auto;" v-if="isUpdatte">
<a-progress type="circle" :percent="percent" :format="percent => `${percent} %`" />
</div>
<div style="display:flex;justify-content: space-between;" v-if="!isUpdatte">
<a-button type="primary" @click="isUpdatte=true" style="margin:30px auto;">
更新新版本
</a-button>
</div>
</a-modal>
</div>
</template>
<script>
import zh_CN from 'ant-design-vue/lib/locale-provider/zh_CN';
import moment from 'moment';
import 'moment/locale/zh-cn';
import {
ConfigProvider,
progress,
button
} from 'ant-design-vue'
// const electron = require('electron').ipcRenderer;
const ipcRenderer = window.ipcRenderer
moment.locale('zh-cn');
export default {
components: {
AConfigProvider: ConfigProvider,
AProgress: progress,
AButton: button,
},
data() {
return {
zh_CN,
modalVisible: false,
percent: 0,
isUpdatte: false,
};
},
mounted() {
if (ipcRenderer) {
const _this=this
ipcRenderer.on("message", (event, arg) => {
// for (var i = 0; i < arg.length; i++) {
console.log(arg);
if ("update-available" == arg.cmd) {
//显示升级对话框
_this.modalVisible = true;
if(!_this.isUpdatte)return;
} else if ("download-progress" == arg.cmd) {
//更新升级进度
/**
*
* message{bytesPerSecond: 47673
delta: 48960
percent: 0.11438799862426002
total: 42801693
transferred: 48960
}
*/
console.log(arg.message.percent);
let percent = Math.round(parseFloat(arg.message.percent));
_this.percent = percent;
} else if ("error" == arg.cmd) {
_this.modalVisible = false;
_this.$message.error("更新失败");
}
// }
});
}
},
created() {
//间隔1小时检测一次
alert(ipcRenderer)
let _this=this
window.setInterval(() => {
// _this.isUpdatte == true 就是自动更新 获取到新版会自动更新 重新安装程序 推荐手动更新
// _this.isUpdatte == false 就是自动更新
ipcRenderer.send("checkForUpdate",_this.isUpdatte);
}, 10000);
}
};
</script>
<style lang="less">
.ant-popover-message {
* {
font-size: 22px !important;
}
.ant-popover-buttons {
.ant-btn {
font-size: 22px !important;
}
}
}
@font-face {
font-family: AGENCYB;
src: url('./assets/AGENCYB.TTF');
}
@font-face {
font-family: AGENCYR;
src: url('./assets/AGENCYR.TTF');
}
html,
body,
#app {
height: 100%;
width: 100%;
padding: 0;
margin: 0;
}
div {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.box-title {
position: relative;
line-height: 32px;
height: 32px;
font-size: 18px;
padding-left: 15px;
display: flex;
align-items: center;
justify-content: space-between;
font-weight: bold;
&.border {
border-bottom: 1px solid #A7B0C1;
}
&:before {
position: absolute;
left: 0;
top: calc(16px - 8px);
content: '';
display: inline-block;
width: 4px;
height: 18px;
background: #2c3e50;
}
}
.box-content {
height: calc(100% - 32px);
}
.second-title {
position: relative;
line-height: 24px;
height: 24px;
font-size: 16px;
padding-left: 15px;
display: flex;
align-items: center;
justify-content: space-between;
color: #939393;
&:before {
position: absolute;
left: 0;
/*top: calc(12px - 3px);*/
content: '';
display: inline-block;
width: 6px;
height: 6px;
border-radius: 50%;
background: #5190eb;
}
}
.ant-modal-body {
padding-top: 0;
}
</style>
现在获取新版本 直接报错 cmd: “update-not-available”
死活找不到这个问题 全带全部没有问题
原来是 版本号一样 程序判断不需要更新! 明明已经有了 latest.yml 还需要版本号 真的坑
打包的时候 就改一下 package.json 里面 version 就可以了
到这一本就可以实现 自动更新
你以为完了
他现在是会自动 更新了 TM的 自动更新 安装会失败
原因是因为 appid 是一样
所有修改版本号的同时也要修改 appid
每次打包都要修改appid 和版本号 无语了
下面放打包 自动修改 version 和appid代码 我的配置在vue.config.js 里面 你也可以把代码放在 build.js 里面
const path = require('path')
var webpack = require('webpack');
const fs = require('fs');
const demoName="app"
function resolve(dir) {
return path.join(__dirname, dir)
}
//------------------------ 自动修改 版本号 用版本号 做appid
function AddZero(time){
if(time<10){
return "0"+time
}else{
return time
}
}
let packageTxt=fs.readFileSync('./package.json','utf8');
let versionData = packageTxt.split('\n');
let packageJson = JSON.parse(packageTxt);
let VersionArr = packageJson.version.split('.');
let date = new Date();
let today = date.getFullYear()+""+AddZero((date.getMonth()+1))+""+AddZero(date.getDate())
if(today == VersionArr[1]){
VersionArr[2] = parseInt(VersionArr[2])+1
}else{
VersionArr[1] = date.getFullYear()+""+AddZero((date.getMonth()+1))+""+AddZero(date.getDate())
VersionArr[2] = 1;
}
let versionLine = VersionArr.join('.');
for(let i= 0; i<versionData.length;i++){
if(versionData[i].indexOf('"version":')!=-1){
versionData.splice(i,1,' "version": "'+versionLine+'",');
break;
}
}
fs.writeFileSync('./package.json',versionData.join('\n'),'utf8');
//------------------------
module.exports = {
outputDir: 'dist',
publicPath: './',
productionSourceMap: false,
// 简单配置webpack
configureWebpack: {
// provide the app's title in webpack's name field, so that
// it can be accessed in index.html to inject the correct title.
resolve: {
alias: {
'@': resolve('src')
}
},
},
// 更加细粒化的配置webpack
// 取自vue-admin-template
chainWebpack: config => {
config.module
.rule('svg')
.exclude.add(resolve('src/svg-icon'))
.end()
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/svg-icon'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]'
})
.end()
},
pluginOptions: {
electronBuilder: {
// List native deps here if they don't work
// 原生包必须这里声明下
externals: ["serialport"],
// If you are using Yarn Workspaces, you may have multiple node_modules folders
// List them all here so that VCP Electron Builder can find them
nodeModulesPath: ["../../node_modules", "./node_modules"],
nodeIntegration: true,
// 打包配置
builderOptions: {
"appId": demoName+versionLine,
// 发布者名称
productName: demoName,
// 安装包名称,可自行配置
artifactName: demoName+'.exe',
nsis: {
// 一键安装,如果设为true,nsis设置就无意义请直接删除 nsis 配置
oneClick: false,
// true全用户安装【目录为:C:\Program Files (x86)】,false安装到当前用户
perMachine: true,
// 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序。
allowElevation: true,
// 允许修改安装目录
allowToChangeInstallationDirectory: true,
// 创建桌面图标
createDesktopShortcut: true,
// 创建开始菜单图标
createStartMenuShortcut: true,
// 快捷方式的名称,默认为应用程序名称
shortcutName: demoName,
// 安装图标
// installerIcon: "./logo.png",// 安装图标
// uninstallerIcon: "./logo.png",//卸载图标
// installerHeaderIcon: "./logo.png", // 安装时头部图标
},
// files: [
// {
// 'filter': ['**/*']
// }
// ],
// extraFiles: ['./extensions/'],
// asar: false,
publish:[
{
"provider":"generic",
url: "http://192.168.0.191/electron/" // 更新文件服务器地址
}
],
}
}
},
}
如果打包软件报错
这个因为electron 版本问题
yarn add electron // 直接yarn 在安装一遍 不要用npm
最后放上代码
我知道你想说什么 依赖下载失败
我把 node_modules 都上传了
就问你细不细节
项目地址: vue-eletron 自动更新/手动更新 .
点个赞好吗 铁子!