文章目录
#项目地址#美化安装流程#自动更新#
如果想美化安装流程,需访问以下地址,里面会提供两份二次打包所需代码文件,附带的有额外说明文档!
文章对应的项目地址
electron 自动更新文章地址
electron 自动更新项目地址
注意:本文介绍内容对应项目地址的 master 分支,electron 自动更新文章对应的是 master_copy 分支!
1 下载官方模板
注意:修改主进程配置(electron所有配置文件)文件后,需关闭已运行的进程,再启动才会生效,关闭进程再重新启动项目即可看到效果
注意:electron 23 及以上的主版本不再支持Windows 7/8/8.1,如有需要,降低electron版本即可,原文地址: https://www.electronjs.org/zh/blog/windows-7-to-8-1-deprecation-notice
1.1 安装 Electron
npm install -g electron
- 为了验证是否安装成功,可以使用如下的命令。
electron --version
1.2 检查镜像源设置
- electron 有可能存在下载包失败 / 打包时候报下载资源 404、证书失效等等错误,此时需检查
npm
配置
npm config get
- 找到以下区域,查看镜像源配置是否正确
disturl = "https://registry.npmmirror.com/-/binary/node"
electron_mirror = "https://registry.npmmirror.com/-/binary/electron/"
ELECTRON_MIRROR = "https://registry.npmmirror.com/-/binary/electron/"
registry = "https://registry.npmmirror.com/"
strict-ssl = false
- 如果不正确,使用以下方式进行单独修改
npm config set disturl=https://registry.npmmirror.com/-/binary/node
npm config set electron_mirror=https://registry.npmmirror.com/-/binary/electron/
npm config set ELECTRON_MIRROR=https://registry.npmmirror.com/-/binary/electron/
1.3 创建运行项目
- Electron 官方提供了一个简单的项目,可以执行以下命令将项目克隆到本地。
git clone https://github.com/electron/electron-quick-start
- 如果下不下来,那么我建议去
gitee
搜electron-quick-start
,。会有跟github
同步更新的仓库,当然文章对应的项目地址里也会有基础模板,只是不会同步更新
- 然后在项目中执行如下命令即可启动项目。
npm run start
2 自定义显示窗口的默认菜单
- 红框里的默认菜单可能满足不了需求 / 想用中文菜单,那么就来自定义菜单。
2.1 创建自定义菜单文件,写入/使用自定义菜单
2.1.1 创建自定义菜单文件,写入自定义菜单
- 在项目根目录创建
menuConfig.js
文件
-- menuConfig.js --
// 创建菜单模板
module.exports = {
// process.platform 是 Node.js 提供的一个全局变量 'darwin':表示 macOS 系统 'win32':表示 Windows 系统 'linux':表示 Linux 系统
template: [
{
label: '文件',
submenu: [
{
label: '打开', accelerator: 'Ctrl+O',
click () {
console.log("open")
}
},
{ label: '保存' },
{ type: 'separator' },
{ label: '退出', role: 'quit' }
]
},
{
label: '编辑',
submenu: [
{ label: '撤销', role: 'undo' },
{ label: '重做', role: 'redo' },
{ type: 'separator' },
{ label: '剪切', role: 'cut', accelerator: 'CmdOrCtrl+X', },
{ label: '复制', role: 'copy' },
{ label: '粘贴', role: 'paste' },
{ label: '删除', role: 'delete' },
{ type: 'separator' },
{ label: '全选', role: 'selectAll' }
]
},
{
label: '查看',
submenu: [{
label: '重载',
accelerator: (function () {
if (process.platform === 'darwin') {
return 'Ctrl+Command+A'
} else {
return 'F5'
}
})(),
click: function (item, focusedWindow) {
// if (focusedWindow) {
// // 重载之后, 刷新并关闭所有的次要窗体
// if (focusedWindow.id === 1) {
// BrowserWindow.getAllWindows().forEach(function (win) {
// if (win.id > 1) {
// win.close()
// }
// })
// }
focusedWindow.reload()
// }
}
}, {
label: '切换开发者工具',
accelerator: (function () {
// 判断是否是macOS系统
if (process.platform === 'darwin') {
return 'Alt+Command+I'
} else {
return 'Ctrl+Shift+I'
}
})(),
click: function (item, focusedWindow) {
if (focusedWindow) {
focusedWindow.toggleDevTools()
}
}
}, {
type: 'separator'
}, {
label: '应用程序菜单演示',
click: function (item, focusedWindow) {
if (focusedWindow) {
const options = {
type: 'info',
title: '应用程序菜单演示',
buttons: ['好的'],
message: '此演示用于 "菜单" 部分, 展示如何在应用程序菜单中创建可点击的菜单项.'
}
dialog.showMessageBox(focusedWindow, options, function () { })
}
}
}]
},
{
label: '窗口',
role: 'window',
submenu: [{
label: '最小化',
accelerator: 'CmdOrCtrl+M',
role: 'minimize'
}, {
label: '关闭',
accelerator: 'CmdOrCtrl+W',
role: 'close'
}, {
type: 'separator'
}, {
label: '重新打开窗口',
accelerator: 'CmdOrCtrl+Shift+T',
enabled: false, // 是否禁用 false 禁用 true
key: 'reopenMenuItem',
click: function () {
app.emit('activate')
}
}]
},
{
label: '帮助',
submenu: [
{
label: '关于', click: function () {
shell.openExternal('http://electron.atom.io')
}
}
]
}
],
}
2.1.2 使用自定义菜单
-- main.js --
const { app, BrowserWindow, Menu } = require('electron')
const { template } = require('./menuConfig');
// 创建菜单对象
const menu = Menu.buildFromTemplate(template);
// 设置应用程序菜单
Menu.setApplicationMenu(menu);
3 项目运行后展示Vue项目,打包为exe程序
3.1 项目运行后展示Vue项目
- 将自己的Vue项目打包,并将文件拷贝到
electron
项目的根目录
-- main.js --
// 我的打包后的文件名叫 vue-dist 根据自己的文件名自行修改
global.__static = path.join(__dirname, '/vue-dist').replace(/\\/g, '\\\\')
mainWindow.loadURL(`${global.__static}/index.html`)
3.2 开发过程中动态更新页面改动
- 上面想让electron展示Vue项目只能运行打包后的效果,那么开发过程中想实时看到效果就需要按以下配置
// global.__static = path.join(__dirname, '/vue-dist').replace(/\\/g, '\\\\')
// mainWindow.loadURL(`${global.__static}/index.html`)
-- 变成 --
// 根据自己的本地地址修改
mainWindow.loadURL('http://localhost:8001')
- 现在改动Vue代码,electron也会同步更新,这样正常用网页端去调试代码。
3.3 通过环境变量去区分生产/开发环境
- app.isPackaged 生产环境 返回true ,否则返回false,那么可以将上边写的配置进行区分
// 加载应用程序
if (!app.isPackaged) {
// 读取文件url地址 - 开发用 生产环境 返回true ,否则返回false
mainWindow.loadURL('http://localhost:8001')
} else {
// 读取本地文件地址 - 打包用
global.__static = path.join(__dirname, '/vue-dist').replace(/\\/g, '\\\\')
mainWindow.loadURL(`${global.__static}/index.html`)
}
3.4 打包为exe程序
3.4.1 无需安装的exe程序
- 修改读取资源位置
// 开发用
// mainWindow.loadURL('http://localhost:8001')
// 打包用
global.__static = path.join(__dirname, '/vue-dist').replace(/\\/g, '\\\\')
mainWindow.loadURL(`${global.__static}/index.html`)
- *在
package.json
文件自定义打包命令
// electron 打包出来的项目名, platform=win32 windows 系统, --arch=x64 系统版本, --icon 图标路径, --out打包后输出文件路径, app-version=0.0.1 版本号
"package": "electron-packager . electron --platform=win32 --arch=x64 --icon=./icon/icon.ico --out=./out-dist --asar --app-version=0.0.1 --overwrite --ignore=node_modules"
"scripts": {
"start": "electron .",
"package": "electron-packager . electron --platform=win32 --arch=x64 --icon=./icon/icon.ico --out=./out-dist --asar --app-version=0.0.1 --overwrite --ignore=node_modules"
},
- 运行打包命令
npm run package
// 如果报错 Cannot find module 'electron,更新下包即可, 更新后再执行 npm run package
// npm initall
// npm run package
- 由于electron装的版本不同,对Node版本的限制也不一样,如果Node版本不对,控制台会给出提示,自行升级版本即可。
- 打包后会在项目根目录
out-dist/electron-win32-x64
中找到已经打包好的exe文件,双击执行即可。
3.4.2 自定义安装的exe程序
- 上边的无法自定义安装位置,总会觉得不像是个exe程序,那么下边将会描述如何改成双击后执行自定义安装。
- 在项目根目录创建
electron-builder.json
文件
{
"appId": "exe-dome.example.myapp", // 指定应用程序的唯一标识符,通常使用逆域名表示,用于在不同平台上标识应用程序
"productName": "exe-dist", // 指定生成的安装程序的名称。
"directories": {
"output": "exe-dist" // 指定输出目录,即生成的安装程序的存放位置。
},
"win": {
"target": "nsis", // 指定打包的目标格式为 NSIS(Nullsoft Scriptable Install System)安装程序。
"icon": "./icon/icon.jpg", // 指定应用程序的图标文件路径。
"artifactName": "${productName} Setup ${version}.${ext}" // 指定生成的安装程序的文件名格式 ${productName} 会被替换为应用程序名称,${version} 会被替换为应用程序版本号,${ext} 会被替换为文件扩展名。
},
"nsis": {
"oneClick": false, // 禁用一键安装模式,用户需要手动选择安装选项。
"perMachine": true, // 将安装程序安装为所有用户,而不是当前用户。
"allowToChangeInstallationDirectory": true // 允许用户选择安装目录。
}
}
- 下载所需npm包
npm i electron-builder -D
- 在
package.json
文件自定义打包命令
// "build": "electron-builder"
"scripts": {
"start": "electron .",
"package": "electron-packager . electron --platform=win32 --arch=x64 --icon=./icon/icon.ico --out=./out-dist --asar --app-version=0.0.1 --overwrite --ignore=node_modules",
"build": "electron-builder"
},
注意:每次打包前建议确认上一次打包出来的文件已被手动删除,如果还存在,那么将其手动删除后再打包
- 执行自定义安装打包命令
npm run build
- 打包后在根目录
exe-dist
文件夹即可找到exe程序,双击执行即可出现安装程序 注意:安装后,此exe程序是真实安装到了电脑上,并会生成注册表文件,需手动卸载。
4 由渲染进程Vue来控制主进程electron的,全屏/半屏,最小化,关闭,最小化到系统托盘等。
- 现在项目的菜单是这个样子的,跟熟知的exe菜单完全不符合,现在希望更加美观,并且更加灵活,那么就需要由界面来控制菜单了。
4.1 隐藏窗口的默认菜单
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
frame: false, // 指定是否显示窗口的默认菜单项。设置为 true 表示显示,默认为 true。
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
4.1.1 隐藏后效果
4.1.2 窗口其他配置项
width: 1200, // 窗口宽度
height: 600, // 窗口高度
// minWidth: 800, // 窗口的最小宽度。
// minHeight:600, // 窗口的最小高度。
// maxWidth:800, // 窗口的最大宽度。
// maxHeight:600, // 窗口的最大高度。
// x: 0, // 窗口的横坐标(以像素为单位),默认为屏幕的中心。
// y: 0, // 窗口的纵坐标(以像素为单位),默认为屏幕的中心。
frame: true, // 指定是否显示窗口的默认菜单项。设置为 true 表示显示,默认为 true。
transparent: false, // 指定窗口是否透明,默认为 false。
// backgroundColor: 'pink', // 窗口的背景颜色,可以使用颜色名称、HEX 值或 RGB 值进行设置。
title: "exe", // 窗口的标题,优先级低于加载文件。
// icon: "./icon/icon.jpg", // 窗口的图标路径。
show: true, // 指定窗口创建后是否立即显示,默认为 true。
resizable: true, // 指定窗口是否可以调整大小
center: true, // 指定窗口是否在屏幕中居中,默认为 true。
movable: true, // 指定窗口是否可以移动,默认为 true。
minimizable: true, // 指定窗口是否可以最小化,默认为 true。
maximizable: true, // 指定窗口是否可以最大化,默认为 true。
closable: true,//指定窗口是否可以关闭,默认为 true。
fullscreenable: true, // 指定窗口是否可以进入全屏模式,默认为 true。
alwaysOnTop: false, // 指定窗口是否始终在其他窗口之上,默认为 false。
fullscreen: false, // 指定窗口是否可以进入全屏模式,默认为 true -- 会导致最小化/半屏/关闭按钮隐藏。
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
4.2 向渲染进程Vue暴漏主进程的方法
- 找到项目根目录的
preload.js
文件
-- preload.js --
/**
* The preload script runs before. It has access to web APIs
* as well as Electron's renderer process modules and some
* polyfilled Node.js functions.
*
* https://www.electronjs.org/docs/latest/tutorial/sandbox
*/
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const type of ['chrome', 'node', 'electron']) {
replaceText(`${type}-version`, process.versions[type])
}
})
const { contextBridge, ipcRenderer } = require("electron")
//向渲染进程(vue)暴露一些属性和方法
contextBridge.exposeInMainWorld('versions', {
// 最小化 / 最小化到系统托盘
minimize(val) {
ipcRenderer.send('minimize', val)
},
// 窗口最大化
windowStatus() {
ipcRenderer.send('windowStatus')
},
// 关闭当前窗口
closeWin() {
ipcRenderer.send('closeWin')
},
})
4.3 在Vue项目制定自己想要的菜单,执行主进程暴漏的方法
- 都到Vue了,这个菜单展示形式,样式,展示逻辑等等,岂不是随心所欲的控制,只需在需要的时刻执行主进程暴漏的方法即可。
- 在Vue项目
/src
下创建header.vue
文件,并引入到App.vue中挂载(我这是示例,写的随意而已。) - 在
header.vue
加入以下代码 ------- 此时已添加拖拽窗口方法 css代码中的-webkit-app-region: drag;
与-webkit-app-region: no-drag;
<template>
<div class="header">
<el-tooltip class="item" effect="dark" content="最小化到系统托盘" placement="bottom">
<span class="no-drag el-icon-circle-close" @click="addMinimize(false)"></span>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="最小化" placement="bottom">
<span class="no-drag el-icon-minus" @click="addMinimize(true)"></span>
</el-tooltip>
<el-tooltip class="item" effect="dark" :content="windowStatus ? '窗口化' : '全屏'" placement="bottom">
<span class="no-drag" :class="windowStatus ? 'el-icon-copy-document' : 'el-icon-full-screen'" @click="addWindowStatus"></span>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="关闭程序" placement="bottom">
<span class="no-drag el-icon-close" @click="addCloseWin"></span>
</el-tooltip>
</div>
</template>
<script>
// 来源根目录 preload.js
const versions = window.versions; // electron 暴漏的主进程事件
export default {
data () {
return {
versions, // 主进程暴漏的事件集合 具体看根目录preload.js文件
windowStatus: false, // 窗口化 true 全屏 false 窗口化
};
},
mounted () { },
methods: {
// 最小化 / 最小化到系统托盘
addMinimize (stata) {
// true 最小化 false 最小化到系统托盘
versions.minimize(stata);
},
// 窗口最大化/还原
addWindowStatus () {
// 窗口状态切换后 -- 切换图标
this.windowStatus = !this.windowStatus;
versions.windowStatus();
},
addCloseWin () {
versions.closeWin();
},
},
};
</script>
<style lang="less">
.header {
background-color: pink;
text-align: right;
// 鼠标拖拽
-webkit-app-region: drag;
span {
padding: 5px 0;
font-size: 20px;
margin-right: 20px;
color: red;
}
}
.no-drag {
// 禁止拖拽
-webkit-app-region: no-drag;
}
</style>
4.4 主进程执行渲染进程触发的事件
4.4.1 最小化窗口/全屏/半屏/关闭当前窗口
-- main.js --
const { ipcMain } = require('electron/main')
let mainWindow
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
frame: false, // 指定是否显示窗口的默认菜单项。设置为 true 表示显示,默认为 true。
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
// 以下事件来源 preload.js
// 最小化窗口
ipcMain.on('minimize', (ev, val) => {
// true 最小化 false 最小化到系统托盘
if (val) {
mainWindow.minimize();
} else {
// mainWindow.hide();
}
})
// 窗口最大化/还原窗口
ipcMain.on('windowStatus', (ev, val) => {
// 判断当前窗口状态 true 最大化
if (mainWindow.isMaximized()) {
mainWindow.restore(); // 还原窗口大小
} else {
mainWindow.maximize(); // 最大化窗口
}
})
// 关闭当前窗口
ipcMain.on('closeWin', (ev, val) => {
mainWindow.close();
})
4.4.2 最小化到系统托盘,并制定小菜单
ipcMain.on('minimize', (ev, val) => {
// true 最小化 false 最小化到系统托盘
if (val) {
mainWindow.minimize();
} else {
// mainWindow.hide();
}
})
--- 变为 ---
ipcMain.on('minimize', (ev, val) => {
// true 最小化 false 最小化到系统托盘
if (val) {
mainWindow.minimize();
} else {
mainWindow.hide();
}
})
const { app, BrowserWindow, Menu, Tray, shell } = require('electron')
app.whenReady().then(() => {
let tray = null;
// 设置最小化到系统托盘后的图标
tray = new Tray(path.join(__dirname, './icon/icon.jpg'));
// 设置最小化到系统托盘后的小菜单
const contextMenu = Menu.buildFromTemplate([
{
label: '打开应用', click: () => {
mainWindow.show();
}
},
{
label: '项目地址', click: () => {
shell.openExternal('https://gitee.com/kuxiao-smile/exe-demo');
}
},
{ type: 'separator' },
{
label: '退出', click: () => {
app.quit();
}
}
]);
// 设置最小化到系统托盘后的悬浮名
tray.setToolTip('我的应用');
// 将设置的小菜单 加入菜单模板
tray.setContextMenu(contextMenu);
// 系统托盘小图标点击事件
tray.on('click', () => {
mainWindow.show(); // 点击系统托盘图标时打开应用程序
});
// 创建窗口
createWindow()
app.on('activate', function () {
// 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()
})
})
5 添加浏览器扩展程序
5.1 打开控制台
- 按键盘
ctrl+shift+I
,即可打开控制台,但如果想使用Vue扩展就得进行额外配置
5.2 添加Vue扩展程序
app.whenReady().then(async () => {
// 创建窗口
createWindow()
app.on('activate', async () => {
// 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()
})
// 判断是否生产环境
// 读取本地浏览器扩展,并添加到控制台 -- 非必要不要使用,会导致浏览器本来装的扩展文件损坏
if (!app.isPackaged) { // 开发环境启用 vue-devtools 扩展 /nhdogjmejiglipccpnnnanhbledajbpd/6.5.0_0 / 扩展ID/版本号自行修改
const vueDevToolsPath = path.join(
os.homedir(),
'/AppData/Local/Google/Chrome/User Data/Default/Extensions/nhdogjmejiglipccpnnnanhbledajbpd/6.5.0_0'
)
await session.defaultSession.loadExtension(vueDevToolsPath)
}
})
6 添加可执行外部脚本
const { execFile } = require('child_process');
app.whenReady().then(async () => {
// 创建窗口
createWindow()
app.on('activate', async () => {
// 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()
})
// 执行外部脚本处理文件 -- 无需要 - 可注释
execFile(path.join(__dirname, '/微信多开.bat').replace(/\\/g, '\\\\'), (error, stdout, stderr) => {
if (error) {
console.error(`执行批处理文件时出错:${error.message}`);
return;
}
console.log('批处理文件执行成功');
console.log('标准输出:', stdout);
console.error('错误输出:', stderr);
});})
- 微信多开内容
// 根据自己的微信路径自行修改,改完把文件后缀改成bat即可
start D:\WeChat\WeChat.exe
start D:\WeChat\WeChat.exe