Electron 是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发经验。
node:v20.12.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`
问题06:需要窗口显示但不需要聚焦
解决方法:
mainWindow.showInactive() // 窗口显示但不聚焦
mainWindow.flashFrame(false) // 强制取消闪烁