用electron 打包发布集成vue2.0项目的操作过程
作者:玲子的猫 发布时间:2024-05-09 15:22:59
手里有个老项目是基于element admin框架下的,之前写的时候没考虑到要打包成桌面端,后期需要打包成客户端,然后就开始了一些列版本操作,看着百度了都很简单,把electron 加入到自己项目中各种不兼容,升级版本,改代码各种百度,一个国庆假期就搞了这个事情,为了后面大家少踩点坑,打算详细的写写我的踩坑之路还有版本配置(版本配置真的很有必要,不要嫌麻烦,一步一步走哈)
1.大家比较关注的版本配置表
node.js | v15.0.0 |
electron | V14.2.9 |
vue | 2.7.8 |
sass-loader | 8.0.2 |
1.如果环境中没有安装cnpm 建议安装下,毕竟electron 用npm 安装基本99% 是安装失败的,别太侥幸了,已经走了很多遍了我(版本太高也会报错)
npm install cnpm@7.1.0 -g
2.安装 electron 环境
(如果 输入 vue add electron-buider 没反应需要 vue/cli升级下这个东西)
(1).在终端输入命令
Electron安装
npm install electron -g
Vue项目添加Electron-builder打包工具
vue add electron-builder
(2)设置镜像方法(不配置也会报错,什么githhub)
npm config edit
使用该命令会弹出npm的配置文档,将以下类容复制到文件末尾。
electron_mirror=https://npm.taobao.org/mirrors/electron/
electron-builder-binaries_mirror=https://npm.taobao.org/mirrors/electron-builder-binaries/
3.测试下的代码
npm run serve--网页
npm run electron:serve--客户端
4.打包命令:npm run electron:build
打包完成了之后,会出.exe
这个就很顺利的操作了
我遇到的bug总结
打包好了之后启动,发现后台连不上,这个地方好像
不需要代理了。问就不知道啥原因
好不容易登录上去了,发现路由不跳转(真的晕死在厕所算了我)
好了好 了改个mode:hash 就ok了
还是点不动还需要改改cookie:
const TokenKey = 'Admin-Token'
// if (process.env.NODE_ENV === 'production') {
// store = new Store()
// }
export function getToken() {
return localStorage.getItem(TokenKey)
}
export function setToken(token) {
return localStorage.setItem(TokenKey, token)
}
export function removeToken() {
// if (process.env.NODE_ENV === 'production') {
// return store.delete(TokenKey)
// }
return localStorage.removeItem(TokenKey)
}
最后忘记了该有的background.js和preload 两个文件代码(放在src 文件夹底下)(这个有啥用呢,在package.json 文件中加入一句 "main": "background.js",)
import {
app,
protocol,
BrowserWindow,
ipcMain,
Menu,
dialog,
globalShortcut
} from 'electron'
import {
createProtocol
} from 'vue-cli-plugin-electron-builder/lib';
import installExtension, {
VUEJS3_DEVTOOLS
} from "electron-devtools-installer";
const fs = require('fs') // 引入node原生fs模块
const os = require('os')
const isDevelopment = process.env.NODE_ENV !== 'production';
let mainWindow
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([{
scheme: 'app',
privileges: {
secure: true,
standard: true
}
}]);
const path = require('path')
const menus = [{
label: '视图',
submenu: [{
label: '刷新',
role: 'reload'
},
{
label: '退出',
role: 'quit'
}
]
}]
const menu = Menu.buildFromTemplate(menus)
function createWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
show: false,
webPreferences: {
enableRemoteModule: true, // 允许弹框
webSecurity: false,
nodeIntegration: true,
nodeIntegrationInWorker: true, // 在Web工作器中启用了Node集成
preload: path.join(__dirname, 'preload.js'),
defaultEncoding: 'utf-8'
}
})
mainWindow.once('ready-to-show', () => {
mainWindow.show()
})
if (process.env.WEBPACK_DEV_SERVER_URL) { // 开发环境
// Load the url of the dev server if in development mode
mainWindow.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
if (!process.env.IS_TEST) mainWindow.webContents.openDevTools();
globalShortcut.register('CommandOrControl+Shift+i', function () { // 使用快捷键shift+ctrl+i 调起开发工具
mainWindow.webContents.openDevTools()
})
} else { // 生产环境
// mainWindow.webContents.openDevTools() // 生产环境关闭调试工具
// Load the index.html when not in development
createProtocol('app')
// Load the index.html when not in development
mainWindow.loadURL('app://./index.html')
}
Menu.setApplicationMenu(menu)
}
/**
* 初始化
*/
app.whenReady().then(() => {
createWindow()
app.setAppUserModelId('管理平台')
// 点击MacOS底部菜单时重新启动窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})
// 兼容MacOS系统的窗口关闭功能
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
mainWindow = null
})
// 最小化窗口(自定义导航条时)
ipcMain.on('window-min', () => {
mainWindow.minimize()
})
// 最大化窗口(自定义导航条时)
ipcMain.on('window-max', () => {
// 如果已经是最大化窗口就还原
if (mainWindow.isMaximized()) {
mainWindow.restore();
} else {
mainWindow.maximize()
}
})
// 关闭窗口
ipcMain.on('window-close', () => {
mainWindow.close()
mainWindow = null;
app.exit();
})
// 主进程给进程通信
ipcMain.on('toMain', function (event, arg) {
event.sender.send('fromMain', arg); // 返回给渲染进程
});
// 下载进程
ipcMain.on('downLoad', function (event, arg) {
mainWindow.webContents.downloadURL(arg.url);
});
// 调用文件读入方法
ipcMain.on('judgeUse', function (event, arg) {
// 读入文件
// 异步返回
fs.readFile('./authorize.bin', {
encoding: 'utf-8'
}, (err, data) => {
// if (err) {
// // dialog.showMessageBox({
// // type: 'error',
// // title: '找不到authorize.bin文件',
// // message: err.path,
// // buttons: ['ok']
// // }).then((index) => {
// // if (index.response === 0) {
// // mainWindow.close()
// // mainWindow = null;
// // app.exit();
// // }
// // })
// event.sender.send('fromMain', null); // 返回给渲染进程
// } else {
// event.sender.send('fromMain', data); // 返回给渲染进程
// }
event.reply('authorizeBack', data); // 返回给渲染进程
})
});
// 读取本地服务的IP 地址 同步
ipcMain.on('syncGetLocalServer', function (event, arg) {
// 读入文件,同步返回数据
fs.readFile('./localServer.xml', {
encoding: 'utf-8'
}, (err, data) => {
event.returnValue = data; // 返回给渲染进程
})
});
// 读取本地服务的IP 地址 异步
ipcMain.on('asyncGetLocalServer', function (event, arg) {
// 读入文件
// 异步返回
fs.readFile('./localServer.xml', {
encoding: 'utf-8'
}, (err, data) => {
event.reply('asyncBackLocalServer', data); // 返回给渲染进程
})
});
// 隐藏按钮
ipcMain.on('hideMenu', function (event, arg) {
mainWindow.setMenu(null);
});
// 显示按钮
ipcMain.on('showMenu', function (event, arg) {
mainWindow.setMenu(menu);
});
app.on("ready", async () => {
if (isDevelopment && !process.env.IS_TEST) {
try {
await installExtension(VUEJS3_DEVTOOLS);
} catch (e) {
console.error("Vue Devtools failed to install:", e.toString());
}
}
await 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();
});
}
}
import {
contextBridge,
ipcRenderer
} from 'electron'
import {
autoUpdater
} from 'electron-updater'
window.ipcRenderer = ipcRenderer
contextBridge.exposeInMainWorld('ipcRenderer', {
// 异步向主进程 发送消息
send: (channel, data) => {
const validChannels = ['toMain', 'downLoad', 'judgeUse', 'hideMenu', 'showMenu', 'window-close', 'asyncGetLocalServer']
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data)
}
},
// 同步向主进程 发送消息,
sendSync: (channel, data) => {
const validChannels = ['syncGetLocalServer']
if (validChannels.includes(channel)) {
return ipcRenderer.sendSync(channel, data)
}
},
// 异步接收主进程返回的数据
receive: async (channel) => {
const validChannels = ['authorizeBack', 'asyncBackLocalServer']
if (validChannels.includes(channel)) {
return new Promise((resolve) => {
ipcRenderer.on(channel, (event, ...args) => {
resolve(...args)
})
});
}
}
})
// 检测更新,在你想要检查更新的时候执行,renderer事件触发后的操作自行编写
! function updateHandle() {
let message = {
error: '检查更新出错',
checking: '正在检查更新……',
updateAva: '检测到新版本,正在下载……',
updateNotAva: '现在使用的就是最新版本,不用更新',
};
const uploadUrl = "http://61.4.184.177:7799/download/"; // 下载地址,不加后面的**.exe
autoUpdater.setFeedURL(uploadUrl);
autoUpdater.on('error', function (error) {
sendUpdateMessage(message.error)
});
autoUpdater.on('checking-for-update', function () {
sendUpdateMessage(message.checking)
});
autoUpdater.on('update-available', function (info) {
sendUpdateMessage(message.updateAva)
});
autoUpdater.on('update-not-available', function (info) {
sendUpdateMessage(message.updateNotAva)
});
// 更新下载进度事件
autoUpdater.on('download-progress', function (progressObj) {
mainWindow.webContents.send('downloadProgress', progressObj)
})
autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
ipcMain.on('isUpdateNow', (e, arg) => {
console.log(arguments);
console.log("开始更新");
//some code here to handle event
autoUpdater.quitAndInstall();
});
mainWindow.webContents.send('isUpdateNow')
});
ipcMain.on("checkForUpdate", () => {
//执行自动更新检查
autoUpdater.checkForUpdates();
})
}()
// 通过main进程发送事件给renderer进程,提示更新信息
function sendUpdateMessage(text) {
mainWindow.webContents.send('message', text)
}
来源:https://blog.csdn.net/ypz131023/article/details/127203251


猜你喜欢
- 什么是MD5?MD5信息摘要算法(英语:MD5 Message-Digest Agorithm),一种被广泛使用的密码散列函数,可以产生出一
- 相信许多小伙伴都玩过数字 * 游戏,就是指在一定数字范围(一般是整数,不包含边界)里,一个玩家选中一个数字当作 * ,其余玩家在这个范围猜数字,
- 通常的做法就是var jsonData = eval(xmlHttp.responseText)。这看起来似乎一切都是正确的,但当你运行代码
- 一、问题背景纸面上有一枚一元钱的银币,你能在 Canny 和 Hough 的帮助下找到它的坐标方程吗?确定一个圆的坐标方程,首先我们要检测到
- 服务:# chkconfig --list 列出所有系统
- 本文实例讲述了python使用xlrd模块读写Excel文件的方法。分享给大家供大家参考。具体如下:一、安装xlrd模块 到python官网
- 前言今天就来理一理session、cookie、token这三者之间的关系!1.为什么会有它们?我们都知道 HTTP 协议是无状态的,所谓的
- 本文介绍了python Celery定时任务的示例,分享给大家,具体如下:配置启用Celery的定时任务需要设置CELERYBEAT_SCH
- 目前防采集的方法有很多种,先介绍一下常见防采集策略方法和它的弊端及采集对策: 一、判断一个IP在一定时间内对本站页面的访问次数,如果明显超过
- 本文实例讲述了python使用append合并两个数组的方法。分享给大家供大家参考。具体如下:lista = [1,2,3]listb =
- 1. 信号与槽(Signals and slots)信号与槽机制是 PyQt 的核心机制,用于对象之间的通信,也就是实现函数之间的自动调用。
- 删除字符串最后一个字符的方法1.使用strip()方法删除最后一个字符Python strip() 方法用于移除字符串头尾指定的字符(默认为
- 本文实例讲述了python中查看变量内存地址的方法。分享给大家供大家参考。具体实现方法如下:这里可以使用id>>> pri
- 用DIV+CSS可以作出很多不同形状的角形;以下我只写了几个;CSS没有优化;是为了让大家看得更清一些;以下是一些小三角的形状:这是第一个小
- 操作说明:选择多个PDF文件,执行完合并后会生成一个新的PDF文件,这个新的PDF文件包含所有源PDF文件的页面。将相关的三方模块导入到代码
- 插件说明:插件根据提供的10位ISBN书号,在Amazon网站上查找该图书的详细信息。如果找到结果,则返回一个两元素的数组,其中第一个元素是
- 图像的全景拼接包括三大部分:特征点提取与匹配、图像配准、图像融合。1、基于SIFT的特征点的提取与匹配利用Sift提取图像的局部特征,在尺度
- 前言PyEMD是经验模态分解 (EMD)及其变体的Python实现,EMD最流行的扩展之一是集成经验模态分解 (EEMD),它利用了噪声辅助
- 在对二维数据进行 resize / mapping / 坐标转换等操作时,经常会将原本的整数坐标变换为小数坐标,对于非整数的坐标值一种直观有
- 有两种方式:一种是图片放在static中,一种是图片放在media中第一种:即:STATIC_URL = '/static/'