使用 Vue + Electron + electron-builder 搭建一个简单的 Electron 项目。本示例只对 Windows 打包进行了部分配置,更多配置可以查看 electron-builder 官网。如果遇到 依赖安装失败 或者 打包失败 的问题,建议多尝试几次。
Electron 依赖 安装失败原因:
1. Node 版本过低
可以在 Electron 官网 版本发布页 查看对应的 Node 版本。如果没有安装过 Node ,建议直接安装 nvm(Windows、Linux/MacOS),使用 nvm 下载 和 切换 Node 版本。
2. npm 没有使用国内镜像源
# 查看当前 npm 源
npm config get registry
# 设置阿里云镜像源
npm config set registry https://registry.npmmirror.com
1. 创建 Vue 项目
npm create vue@latest
2. 安装 Electron 依赖
npm install --save-dev electron
3. 安装 electron-builder 依赖
npm install --save-dev electron-builder
4. 安装 electron-updater 依赖
npm install electron-updater
5. 配置 package.json
/package.json
5.1 dependencies 与 devDependencies 由 npm 自动生成,不需要改动。
5.2 实际代码中需要把注释删掉。
{
"name": "shadiao",
"version": "0.0.0",
"description": "沙雕",
"type": "module",
// 主进程
"main": "./electron/main.js",
"scripts": {
"dev": "vite",
// 启动 Electron
"start": "electron .",
// 打包
"build": "vite build && electron-builder",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
"format": "prettier --write src/"
},
"dependencies": {
"electron-updater": "^6.1.8",
"pinia": "^2.1.7",
"vue": "^3.4.21",
"vue-router": "^4.3.0"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.8.0",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/eslint-config-prettier": "^9.0.0",
"electron": "^30.0.0",
"electron-builder": "^24.13.3",
"eslint": "^8.57.0",
"eslint-plugin-vue": "^9.23.0",
"prettier": "^3.2.5",
"vite": "^5.2.8",
"vite-plugin-vue-devtools": "^7.0.25"
},
// 作者信息
"author": {
"name": "shadiao",
"email": "shadiao@shadiao.com"
},
// 软件协议
"license": "MIT",
// 打包配置
"build": {
// 应用名称
"productName": "沙雕",
// 应用唯一标识
"appId": "com.shadiao.shadiao",
"directories": {
// 打包文件存放位置
"output": "build"
},
// 将源代码打包成 asar 包
"asar": true,
// windows 配置
"win": {
// 应用图标
"icon": "./public/shadiao.ico",
// 安装包名称
"artifactName": "${productName}-${platform}-${arch}-${version}.${ext}",
"target": [
{
// 打包为 nsis
"target": "nsis",
// 适用 x64 架构
"arch": [
"x64"
]
}
]
},
"nsis": {
// 是否一键安装
"oneClick": false,
// 允许请求提升权限
"allowElevation": true,
// 允许自定义安装目录
"allowToChangeInstallationDirectory": true,
// 创建桌面快捷方式
"createDesktopShortcut": true
},
"publish": {
// 发布类型,generic 表示私有服务,也可以使用 github
"provider": "generic",
// 服务 url,填写自己服务器的 url(http/https + :// + 域名/ip + : + 端口号 (默认端口号为 80))
"url": "http://www.wurenjingjie.com"
}
}
}
6. 配置 vite.config.js
/vite.config.js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import VueDevTools from 'vite-plugin-vue-devtools'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
VueDevTools(),
],
// 防止打包后找不到渲染页面导致白屏
base: './',
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
7. 在根目录创建 electron 目录
/electron
8. main.js - Electron 主进程
/electron/main.js
import { app, BrowserWindow } from 'electron'
import path from 'path'
import { fileURLToPath } from 'url'
import { window } from './window.js'
import { tray } from './tray.js'
import { updater } from './updater.js'
let win = null
// 创建窗口
const createWindow = () => {
win = new BrowserWindow({
// 窗口宽度
width: 1050,
// 窗口高度
height: 800,
// 最小窗口宽度
minWidth: 800,
// 最小窗口高度
minHeight: 750,
// 窗口居中
center: true,
// 窗口边框
// frame: false,
// 不显示窗口
show: false,
// 页面配置
webPreferences: {
// 预加载脚本配置
preload: path.join(path.dirname(fileURLToPath(import.meta.url)), './preload.js')
}
})
// 优雅地显示窗口
win.once('ready-to-show', () => {
win.show()
})
// 根据 app 是否打包来判断 开发环境 和 生产环境
if (!app.isPackaged) {
// 开发环境
win.loadURL('http://localhost:5173')
} else {
// 生产环境
win.loadFile(path.join(path.dirname(fileURLToPath(import.meta.url)), '../dist/index.html'))
}
// 打开开发者工具
// win.webContents.openDevTools()
// 禁用菜单
win.setMenu(null)
window(win)
tray(win)
updater(win)
}
// app 准备完毕
app.whenReady().then(() => {
// 请求单例锁,防止同时打开多个程序
if (!app.requestSingleInstanceLock()) {
app.quit()
return
}
createWindow()
// 第二个程序实例被创建
// event - 第二个程序实例触发的事件
// argv - 第二个程序实例命令行参数的数组
// workingDirectory - 第二个程序实例的工作目录
// additionalData - 第二个程序实例发送过来的额外的 JSON 对象
app.on('second-instance', (event, argv, workingDirectory, additionalData) => {
console.log(additionalData)
// 窗口是否对用户可见
if (win.isVisible()) {
// 聚焦窗口
win.focus()
} else {
// 显示窗口
win.show()
}
})
// 在 maxOS 上,当单击停靠图标并且没有其他窗口打开时,通常会在应用程序中重新创建一个窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})
9. window.js - Electron 窗口配置
/electron/window.js
import { ipcMain } from 'electron'
export function window(win) {
// 最小化窗口
ipcMain.on('min-window', () => {
win.minimize()
})
// 最大化窗口
ipcMain.on('max-window', () => {
win.maximize()
})
// 恢复窗口大小
ipcMain.on('restore-window', () => {
win.unmaximize()
})
// 窗口最大化
win.on('maximize', () => {
win.webContents.send('window-max')
})
// 窗口恢复大小
win.on('unmaximize', () => {
win.webContents.send('window-restore')
})
// 关闭窗口
ipcMain.on('close-window', () => {
win.close()
})
// 改变 win.close() 默认事件
win.on('close', (event) => {
event.preventDefault()
win.hide()
})
}
10. tray.js - Electron 托盘配置
/electron/tray.js
import { app, Tray, Menu } from 'electron'
import path from 'path'
import { fileURLToPath } from 'url'
export function tray(win) {
let systemTray = null
// 初始化系统托盘
if (!app.isPackaged) {
// 开发环境
systemTray = new Tray(path.join(path.dirname(fileURLToPath(import.meta.url)), '../public/shadiao.ico'))
} else {
// 生产环境
systemTray = new Tray(path.join(path.dirname(fileURLToPath(import.meta.url)), '../dist/shadiao.ico'))
}
// 初始化托盘菜单
const contextMenu = Menu.buildFromTemplate([
{
label: '打开沙雕',
click: () => {
// 窗口是否对用户可见
if (win.isVisible()) {
// 聚焦窗口
win.focus()
} else {
// 显示窗口
win.show()
}
}
},
{
label: '退出沙雕',
click: () => {
// 由于 win.close() 默认事件被修改,使用 win.destroy() 强制关闭窗口
win.destroy()
// 退出程序
app.quit()
}
}
])
// 设置托盘菜单
systemTray.setContextMenu(contextMenu)
// 设置鼠标移入托盘显示文字
systemTray.setToolTip('沙雕')
// 单击托盘事件
systemTray.on('click', () => {
// 窗口是否对用户可见
if (win.isVisible()) {
// 最小化窗口
win.minimize()
} else {
// 显示窗口
win.show()
}
})
}
11. updater.js - Electron 更新配置
/electron/updater.js
import { ipcMain } from 'electron'
import electronUpdater from 'electron-updater'
const { autoUpdater } = electronUpdater
export function updater(win) {
// 关闭自动更新
autoUpdater.autoDownload = false
// 有新版本
autoUpdater.on('update-available', (info) => {
win.webContents.send('update-available', info)
})
// 无新版本
autoUpdater.on('update-not-available', (info) => {
win.webContents.send('update-not-available', info)
})
// 更新出错
autoUpdater.on('error', (error) => {
win.webContents.send('update-error', error)
})
// 下载进度
autoUpdater.on('download-progress', (info) => {
win.webContents.send('download-progress', {
speed: info.bytesPerSecond, // 网速
percent: info.percent, // 百分比
transferred: info.transferred, // 已传大小
total: info.total // 总大小
})
})
// 下载完成
autoUpdater.on('update-downloaded', () => {
win.webContents.send('update-downloaded')
})
// 检测是否有新版本
ipcMain.on('check-update', () => {
autoUpdater.checkForUpdates()
})
// 下载更新包
ipcMain.on('download', () => {
autoUpdater.downloadUpdate()
})
// 关闭程序,安装更新包
ipcMain.on('quit-and-install', () => {
autoUpdater.quitAndInstall()
})
}
12. preload.js - Electron 预加载脚本
/electron/preload.js
/* eslint-disable no-undef */
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
// 最小化窗口
minWindow: () => {
ipcRenderer.send('min-window')
},
// 最大化窗口
maxWindow: () => {
ipcRenderer.send('max-window')
},
// 恢复窗口大小
restoreWindow: () => {
ipcRenderer.send('restore-window')
},
// 窗口最大化
windowMax: (event) => {
ipcRenderer.on('window-max', event)
},
// 窗口恢复大小
windowRestore: (event) => {
ipcRenderer.on('window-restore', event)
},
// 关闭窗口
closeWindow: () => {
ipcRenderer.send('close-window')
},
// 检测是否有新版本
checkUpdate: () => {
ipcRenderer.send('check-update')
},
// 有新版本
updateAvailable: (event, info) => {
ipcRenderer.on('update-available', event, info)
},
// 无新版本
updateNotAvailable: (event, info) => {
ipcRenderer.on('update-not-available', event, info)
},
// 更新出错
updateError: (event, error) => {
ipcRenderer.on('update-error', event, error)
},
// 下载更新包
download: () => {
ipcRenderer.send('download')
},
// 下载进度
downloadProgress: (event) => {
ipcRenderer.on('download-progress', event)
},
// 下载完成
updateDownloaded: (event) => {
ipcRenderer.on('update-downloaded', event)
},
// 关闭程序,安装更新包
quitAndInstall: () => {
ipcRenderer.send('quit-and-install')
}
})
13. 图标
/public/shadiao.ico
https://gitee.com/wu-ren-jing-jie/shadiao/blob/master/public/shadiao.ico
14. 运行
npm run dev
npm run start
15. 打包
npm run build