Electron 是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发经验。
node:v18.18.2 npm:v9.8.1 electron:v29.1.1
一、使用electron开发
1、初始化项目
npm init
2、安装electron
npm install electron --save-dev
之前的淘宝镜像已经过期的,这里如果报错的话重新配置electron的环境变量
在根目录下创建 .npmrc 文件,配置以下内容:
electron_mirror=https://npmmirror.com/mirrors/electron/
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/
3、创建 index.html 文件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'" />
<meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'" />
<title>Hello from Electron renderer!</title>
</head>
<body>
<h1>Hello from Electron renderer!</h1>
<p>👋</p>
</body>
</html>
4、创建 main.js(electron主进程)文件
const { app, BrowserWindow, nativeImage, Tray, Menu } = require('electron/main')
const { join } = require('path')
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
})
win.loadFile(join(__dirname, './index.html'))
}
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
5、package.json文件配置
// package.json文件
{
"main": "./main.js",
"script": {
"dev": "electron ."
}
}
终端执行 npm run dev 即可运行electron项目
具体请参考electron官网:简介 | Electron
到此为止,我们已经成功启动了electron项目。但是这样创建项目的话我们需要编写原生JS代码,可能对于某些同学来讲不太友好,没关系接下来我们可以使用 Vue3 + Electron 进行开发。
二、使用Vue3 + Electron开发项目
1、创建项目
很多同学可能会使用 npm create vue@latest 先创建 vue3 项目,再安装 electron。这样确实可以,但是需要启动两个终端分别运行 vue 和 electron 项目,还是相对比较麻烦。
这里推荐直接使用 electron-vite 框架。具体请参考:简介 | electron-vite
npm create @quick-start/electron@latest
yarn create @quick-start/electron
pnpm create @quick-start/electron
选择适合的安装命令,然后按照提示操作即可!
✔ Project name: … <electron-app>
✔ Select a framework: › vue
✔ Add TypeScript? … No / Yes
✔ Add Electron updater plugin? … No / Yes
✔ Enable Electron download mirror proxy? … No / Yes
Scaffolding project in ./<electron-app>
... Done.
2、目录结构
3、开启热更新
// package.json文件
{
"scrpit": {
"dev": "electron-vite dev --watch"
}
}
4、更换任务栏图标
// index.js主进程(省略部分代码)
import { BrowserWindow, nativeImage } from 'electron';
import { join } from 'path';
const createWindow = () => {
let mainWindow = new BrowserWindow({
icon: nativeImage.createFromPath(join(__dirname, './xxx.ico'))
})
}
5、系统托盘图标和菜单
// index.js主进程(省略部分代码)
import { Tray, Menu, nativeImage } from 'electron';
import { join } from 'path';
let tray = new Tray(nativeImage.createFromPath(join(__dirname, './xxx.ico')));
let contextMenu = Menu.buildFromTemplate([
{
label: '意见反馈',
click: () => console.log('点击了意见反馈')
},
{
label: '帮助中心',
click: () => console.log('点击了帮助中心')
},
{
label: '更新检测',
click: () => console.log('点击了更新检测')
},
{
label: '退出',
click: () => console.log('点击了退出')
}
])
tray.setToolTip('广东彭于晏之深圳分晏'); // 鼠标悬停托盘系统图标显示
tray.setContextMenu(contextMenu);
tray.on('click', () => mainWindow.show()); // 单机托盘图标打开应用程序
6.1、打包
安装命令
npm install electron-builder --save-dev
在package.json中进行配置(这些会在创建项目时自动配置好)
// package.json文件
{
"script": {
"build": "electron-vite build",
"build:win": "electron-vite && electron-builder --win", // 只打包windows
"build:mac": "electron-vite && electron-builder --mac",
"build:linux": "electron-vite && electron-builder --linux"
}
}
我的项目使用到了c++的第三方库,所以还需要其他插件
npm install electron-rebuild --save-dev
node环境无法加载c语言的第三方库,这里需要重新编译一下node_modules目录
./node_modules/.bin/electron-rebuild
(顺便提一下,node环境打印中文的话可能会出现乱码的情况,在终端输入)
chcp 65001
6.2、配置打包信息
// pageage.json文件(省略部分配置)
{
"build": {
"appId": "xxxxxxx", // 定义应用程序的唯一标识符
"productName": "xxxxxx", // 定义应用程序的名称
"copyright": "Copyright © 2024 ${author}", // 定义应用程序的版权信息
"mac": {
"icon": "./xxxxx.icns", // mac用程序的图标文件路径
"target": [ // 定义构建目标的数组(这里定义了两种目标:dmg(磁盘映像)和 zip(压缩文件)。每种目标都指定了支持的架构)
{
"target": "dmg",
"arch": [
"x64"
]
},
{
"target": "zip",
"arch": [
"x64"
]
}
]
},
"win": {
"icon": "./xxxxx.ico", // windows用程序的图标文件路径
"target": [
{
"target": "nsis",
"arch": [
"x64"
]
}
],
"artifactName": "${productName}_setup_${version}.${ext}", // 定义构建产物的文件名模板
"nsis": {
"oneClick": false, // 取消一键安装,用户可以选择安装目录等
"allowToChangeInstallationDirectory": true, // 是否允许用户更改安装目录
"installerIcon": "./xxxx.ico", // 安装程序的图标
"uninstallerIcon": "./xxxx.ico", // 卸载程序的图标
"installerHeaderIcon": "./xxxx.ico", // 安装程序的头部图标
"createDesktopShortcut": true, // 是否在桌面创建快捷方式
"createStartMenuShortcut": false // 是否在开始菜单创建快捷方式
}
}
},
"files": [] // 定义在构建过程中应包含的文件或文件夹
}
6.3、vue 打包多页面
在electron.vite.config.js目录下进行配置
// electron.vite.config.js文件(省略部分代码)
import { join } from 'path';
import { defineConfig } from 'electron-vite';
export default defineConfig({
base: './', // 打包后的相对路径
build: {
rollupOptions: {
input: { // 打包后目录中就会生成 index.html 和 about.html文件
index: join(__dirname, './src/renderer/index.html'),
about: join(__dirname, './src/renderer/about.html')
},
output: {
assetFileNames: 'assets/[name].[ext]', // 这定义了资源文件(如图片、字体等)的输出文件名格式
chunkFileNames: 'assets/[name]-[hash].js', // 这定义了代码块(例如,由动态导入产生的代码块)的输出文件名格式
entryFileNames: 'assets/[name]-[hash].js' // 这定义了入口文件的输出文件名格式
}
}
}
})
7、electron 应用程序更新
安装命令
npm install electron-updater --save
在 main.js 主进程中编写代码
import { app, dialog, BrowserWindow } from 'electron';
import { autoUpdater } from 'electron-updater';
let mainWindow;
const createWindow = () => {
mainWindow = new BrowserWindow({
width: 1080,
height: 720,
...
})
}
const checkAppVersionUpdate = () => {
// 应用退出后自动安装
autoUpdater.autoInstallOnAppQuit = true;
// 自动下载
autoUpdater.autoDownload = true;
// 检测是否有更新包并通知
autoUpdater.checkForUpdatesAndNotify().catch();
// 下载进度
autoUpdater.on('download-progress', (prog) => {
mainWindow.webContents.send('app-update-version', {
speed: Math.ceil(prog.bytesPerSecond / 1000), // 网速
percent: Math.ceil(prog.percent) // 百分比
})
})
// 安装最新版本
autoUpdater.on('update-downloaded', (info) => {
let dialogOpts = {
type: 'info',
buttons: ['重新启动', '取消'],
title: '应用程序更新',
message: '已下载新版本。重新启动应用程序以安装最新版本',
detail: info.releaseNotes
}
dialog.showMessageBox(dialogOpts).then(result => {
if(result.response == 0) autoUpdater.quitAndInstall();
})
})
}
app.whenReady().then(() => {
createWindow();
setTimeout(() => checkAppVersionUpdate(), 1000);
})
详细内容请参考官网Auto Update - electron-builder
在 package.json 中配置远程更新地址
{
"scripts": {
},
"dependencies": {
},
"devDependencies": {
},
"build": {
"publish": [
{
"provider": "generic",
"url": "http://xxx/xxx:3000/updater/" // 打包后会生成一个latest.yml文件和.exe的二进制文件, 复制到updater目录下
}
],
"directories": {
"output": "electron-dist" // 更改打包输出目录,默认是dist
}
}
}
在 electron-builder.yml 中配置远程更新地址
全局安装serve
npm install -g serve
启动服务
serve electron-dist
启动后默认有个端口号3000的服务,就可以在本地开发环境进行调试
修改 package.json 中的版本号,并打包上传至服务器,程序会对比远程地址中latest.yml的版本号进行自动下载更新(复杂更新消息提示可自行参考官网进行配置,我这里就是个简单的更新安装示例,毕竟3000块有3000块的写法,都是在公司当牛马没必要那么认真对吧)
8、开发过程中踩过的坑
问题 01:可以重复打开多个程序
解决方法:
const gotTheLock = app.requestSingleInstanceLock();
const createWindow = () => {
if(gotTheLock) {
app.on('second-instance', () => {
if(mainWindow.isMinimized()) mainWindow.restore();
mainWindow.focus();
})
} else {
app.quit();
}
mainWindow = new BrowserWindow({ ... });
}
问题 02:窗口调用 hide() 和 show() 事件有明显闪屏现象
解决方法:
监听窗口的ready事件,然后加上
app.whenReady().then(() => {
...
app.commandLine.appendSwitch('wm-window-animations-disabled');
})
问题 03:重启程序
解决方法:
app.relaunch();
app.quit();
问题04:a-select下拉选择框跟随滚动条滚动
解决方法:
在组件中加入 :getPopupContainer="triggerNode => triggerNode.parentNode"
<a-select v-model:value="xxx" :options="[ ]" :getPopupContainer="triggerNode => triggerNode.parentNode"/>
问题05:vue-i18n报错 Invalid linked format
报错原因:
vue-i18n的9以上的版本中@被用作特殊字符处理,直接用会报错
解决方式:
修改前:'xxxxxxx@qq.com'
修改后:邮箱: `xxxxxxx{'@'}qq.com`