背景: 已经开发完的vue2项目,在此基础上进行打包并用进行热更新。
Electron官网
关于nodejs
我一开始用的18版本,但是会有报错,针对报错试了网上能找到的所有方法,都没解决,最终降版本了,降到了v14.21.3版本,至此nodejs问题解决。
关于打包后跳转失效
1、必须使用hash模式,否则打包后路由跳转失效
2、如果项目中用了 js-cookie 插件也是不行的,建议存到本地
3、如果项目中使用了clipboard进行复制在打包后会提示找不到(我没用clipboard,因此删掉了,没找解决办法)
步骤
1、首先需要确保安装了vue CLI
npm install -g @vue/cli
2、安装electron打包模块
vue add electron-builder
本人选择了12的版本,有的网友说13的版本不太稳定,因为忙没去测试。
安装完成后在package.json中会多这两行代码,分别是打包命令和运行命令。
同时会在与main.js同级里面看到background.js,这个文件就是electron的主文件,其中设置窗口的大小位置、热更新等都在这里开发。
3、安装完成后启动看下效果
npm run electron:serve
有个警告,不碍事 -_-
启动成功后会弹出electron的窗口。到此启动成功,未开发的功能就可以继续开发了。
在开发中会自动打开开发者工具,但是打包后就没有了,如果看网络请求和dom之类的可以按照下图的配置打开工具,但是console无法看到,如果要看console可以根据网上配置,也可以用electron的dialog模块
const {dialog} = require('electron')//弹窗的方式
dialog.showMessageBox(mainWin, {
type: 'info',
buttons: ['OK'],
title: '检测更新...',
message: '检测更新'
});
如果要看需要配置,更多窗口配置和其他配置可以看官网,也可以看w3c的 https://www.w3cschool.cn/electronmanual/
const win = new BrowserWindow({
width: 800,//窗口的宽度
height: 600,//窗口的高度
webPreferences: {
nodeIntegration:true,
contextIsolation:false,
webSecurity:false,//是否开启代理
// 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,
// contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION
}
})
// 在主进程中打开开发者工具
win.webContents.openDevTools()
Vue.config.devtools = false
打包
1、运行打包命令 npm run electron:build
成功后会生成一个dist_electron文件夹,里面会有安装文件,如果是为了看打包后的效果,win-unpacked文件夹里面也有一个exe文件,可以不用安装程序查看打包后的效果
electron-updater热更新
1、首先安装electron-updater
npm i electron-updater
2?安装完成后确认更新流程,更新一般是通过版本校验,本地版本低于线上版本的话进行下载线上版本并安装的过程,因此在打包的时候需要同时生成一个yml文件,用来校验版本和定义下载内容,因此需要修改打包文件。有的人通过npm下载的electron-builder会生成一个builder.js文件,但是通过vue add没有这个文件,我们在vue.config.js里面添加就行,放在module.exports里面,代码如下:
特别注意:url很重要,地址要么是http的,如果你用https千万是正规的ssl,不然没用,因为这个找了整整一天的问题
module.exports={
pluginOptions: {
electronBuilder: {
nodeIntegration: true,
builderOptions: {
nsis: {},
asar: false,
"publish": [
{
"provider": "generic",
"url": "https://xxx.xxx.xxx/file/"//你线上服务器的地址,其中file文件夹里面有你下一步生成的yml和exe文件
}
],
}
}
},
}
配置完之后进行打包,完成后会有一个yml文件,这个文件和exe文件 需要你上传到服务器,放在file文件夹里面
3、接下来进行编写更新代码,创建一个update.js,里面代码如下
const { autoUpdater } = require('electron-updater');
const {dialog} = require('electron')
var mainWin = null;
export const checkUpdate = (win, ipcMain) => {
autoUpdater.autoInstallOnAppQuit = true; // 应用退出后自动安装
mainWin = win;
// autoUpdater.checkForUpdatesAndNotify();
//校验地址
autoUpdater.setFeedURL({ provider: "generic",url: 'https://xxx.xxx.xxx/file/' });
autoUpdater.autoDownload = true; // 自动下载
autoUpdater.checkForUpdates();
// 检测是否有更新包并通知
autoUpdater.on('error', (error) => {
// 下载完成后强制用户安装,不推荐
// autoUpdater.quitAndInstall();
});
//监听开始检测更新事件
autoUpdater.on('checking-for-update', function (message) {
console.info('检测更新')
dialog.showMessageBox(mainWin, {
type: 'info',
buttons: ['OK'],
title: '检测更新...',
message: '检测更新'
});
});
//监听发现可用更新事件
autoUpdater.on('update-available', function (message) {
console.info('有可用更新')
dialog.showMessageBox(mainWin, {
type: 'info',
buttons: ['OK'],
title: '下载更新中,请等待...',
message: '下载完成后会自动安装'
});
});
//监听没有可用更新事件
autoUpdater.on('update-not-available', function (message) {
dialog.showMessageBox(mainWin, {
type: 'info',
buttons: ['OK'],
title: '没有更新...',
message: '没有更新'
});
});
autoUpdater.on('download-progress', (prog) => {
//通过preload.js进行通信,来显示进度条
mainWin.webContents.send('update', {
speed: Math.ceil(prog.bytesPerSecond / 1000), // 网速
percent: Math.ceil(prog.percent), // 百分比
});
});
autoUpdater.on('update-downloaded', (info) => {
dialog.showMessageBox(mainWin, {
type: 'info',
buttons: ['OK'],
title: '下载完成',
message: '下载完成'
});
mainWin.webContents.send('downloaded');
// 下载完成后强制用户安装,不推荐
autoUpdater.quitAndInstall();
});
// 监听渲染进程的 install 事件,触发退出应用并安装
ipcMain.handle('install', () => autoUpdater.quitAndInstall());
};
// module.exports = checkUpdate;
这是所有的更新流程,其中会用到的文件代码
preload.js文件需要放在public和dist_electron里面
const { ipcRenderer } = require('electron');
window.elecAPI = {
toInstall: () => ipcRenderer.invoke('install'),
onUpdate: (callback) => ipcRenderer.on('update', callback),
onDownloaded: (callback) => ipcRenderer.on('downloaded', callback),
};
background.js文件
'use strict'
import { app, protocol, BrowserWindow ,ipcMain } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
const path = require('path');
const preload = path.join(__dirname, './preload.js');
import Vue from 'vue'
import {checkUpdate} from './update.js'
var win
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.
win = new BrowserWindow({
width: 1600,
height: 1000,
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,
// contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION
nodeIntegration:true,
contextIsolation:false,
webSecurity:false,
devTools: true,
preload
}
})
// 在主进程中打开开发者工具
win.webContents.openDevTools()
Vue.config.devtools = true
// 检查更新
checkUpdate(win, ipcMain);
// 在渲染进程中打开开发者工具
// if (process.env.NODE_ENV === 'development') {
// Vue.config.devtools = true
// }
// 关闭工具栏
win.setMenu(null)
// 应用全屏
win.setFullScreen(false);
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)
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.loadFile('./index.html')
}
}
// 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()
})
}
}