本文将详细介绍如何将基于 Vue3.2 的项目打包成 Electron 桌面应用。通过结合 Electron 和 Vue CLI 工具链,可以轻松实现跨平台桌面应用的开发与发布。
1. 项目结构说明
项目主要分为以下几个部分:
- electron/main.js:Electron 主进程文件。
- electron/preload.js:Electron 预加载脚本。
- package.json:项目配置文件,包含 Electron 相关依赖和脚本。
- .env 和 .env.electron:环境变量配置文件,分别用于普通模式和 Electron 模式。
- vue.config.js:Vue CLI 的配置文件,用于优化构建和资源路径。
- router.ts:Vue 路由配置文件,支持 hash 和 history 模式切换。
2. 代码详解
npm install electron electron-builder cross-env --save-dev
2.1 electron/main.js
主进程文件负责创建窗口并加载 Vue 应用:
const { app, BrowserWindow, globalShortcut, Menu } = require("electron");
const path = require("path");
let mainWindow;
// 捕获未处理的异常
process.on("uncaughtException", (error) => {
console.error("Uncaught Exception:", error);
});
process.on("unhandledRejection", (reason, promise) => {
console.error("Unhandled Rejection at:", promise, "reason:", reason);
});
function createWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
icon: path.join(__dirname, "src", "assets", "logo_32.ico"), // 设置窗口图标 尺寸:32*32
images: true, // 启用图像支持
show: false, // 初始时不显示窗口,避免闪烁
// frame: false, // 隐藏默认的窗口边框和标题栏
// fullscreen: true, // 启动时全屏
webPreferences: {
preload: path.join(__dirname, "preload.js"), // 预加载脚本(可选)
nodeIntegration: true, // 启用 Node.js 集成(根据需要开启)
// 添加以下配置解决媒体自动播放问题
webSecurity: false, // 禁用同源策略(开发时可关闭,生产环境慎用)
autoplayPolicy: "no-user-gesture-required", // 允许自动播放
contextIsolation: false, // 必须为false才能访问全局变量
},
});
// 加载 Vue 项目的生产构建文件
if (process.env.NODE_ENV === "development") {
mainWindow.loadURL("http://localhost:3000"); // 开发环境(Vue 开发服务器)
} else {
mainWindow.loadFile(path.join(__dirname, "../dist/index.html")); // 生产环境
}
// 窗口最大化
mainWindow.maximize();
// 显示窗口(在最大化后显示)
mainWindow.show();
// 隐藏菜单栏
Menu.setApplicationMenu(null);
// 打开开发者工具(开发时可以打开)
// mainWindow.webContents.openDevTools();
// 打开调试工具
globalShortcut.register("CommandOrControl+Shift+I", () => {
mainWindow.webContents.openDevTools();
});
// 切换全屏
globalShortcut.register("CommandOrControl+Alt+Q", () => {
if (mainWindow) {
const isFullScreen = mainWindow.isFullScreen();
mainWindow.setFullScreen(!isFullScreen);
}
});
// 返回上一页
globalShortcut.register("CommandOrControl+Left", () => {
if (mainWindow) {
const history = mainWindow.webContents.navigationHistory;
if (history.canGoBack()) {
history.goBack();
}
}
});
// 刷新页面
globalShortcut.register("CommandOrControl+R", () => {
if (mainWindow) {
mainWindow.webContents.reload();
}
});
mainWindow.on("closed", () => {
mainWindow = null;
});
}
app.whenReady().then(() => {
createWindow();
// 创建自定义菜单
// const menuTemplate = [
// {
// label: "操作",
// submenu: [
// { type: "separator" },
// { label: "切换全屏", role: "togglefullscreen" },
// { type: "separator" },
// {
// label: "返回上一页",
// accelerator: "CmdOrCtrl+Left",
// click: () => {
// if (mainWindow && mainWindow.webContents.canGoBack()) {
// mainWindow.webContents.goBack();
// }
// },
// },
// ],
// },
// ];
// // 根据模板创建菜单
// const menu = Menu.buildFromTemplate(menuTemplate);
// // 设置应用菜单
// Menu.setApplicationMenu(menu);
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});
// 退出时注销快捷键
app.on("will-quit", () => {
globalShortcut.unregisterAll();
});
2.2 electron/preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
send: (channel, data) => ipcRenderer.send(channel, data),
receive: (channel, func) => ipcRenderer.on(channel, (event, ...args) => func(...args)),
});
2.3 package.json
配置了 Electron 相关的脚本和依赖:
{
"name": "saas_system",
"version": "0.1.35",
"private": true,
"main": "electron/main.js",
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"test:unit": "vue-cli-service test:unit",
"lint": "vue-cli-service lint",
"start": "electron .",
"build:electron": "vue-cli-service build --mode electron",
"electron:serve": "vue-cli-service serve --mode development && electron .",
"electron:build": "vue-cli-service build --mode electron && electron-builder"
},
"dependencies": {
"electron": "^35.1.3",
"electron-builder": "^26.0.12"
},
"build": {
"appId": "com.example.myapp",
"productName": "myapp",
"files": [
"dist/**/*",
"electron/main.js",
"src/assets/logo.ico" // 尺寸:256*256
],
"win": {
"target": "nsis",
"icon": "src/assets/logo.ico" // 尺寸:256*256
},
"mac": {
"target": "dmg"
},
"linux": {
"target": "AppImage"
}
}
}
2.4 .env 和 .env.electron
分别定义了普通模式和 Electron 模式的环境变量:
.env 文件
VUE_APP_ROUTER_MODE=history
VUE_APP_PUBLIC_PATH=/
.env.electron 文件
VUE_APP_ROUTER_MODE=hash
VUE_APP_PUBLIC_PATH=./
2.5 vue.config.js
动态生成资源路径前缀:
const productionGzipExtensions = ["js", "css"];
module.exports = {
publicPath: process.env.VUE_APP_PUBLIC_PATH || "/",
configureWebpack: {
devtool: "source-map",
output: {
filename: `${getAssetsPath()}js/[name].${Timestamp}.js`,
chunkFilename: `${getAssetsPath()}js/[name].${Timestamp}.js`,
},
},
css: {
extract: {
filename: `${getAssetsPath()}css/[name].${Timestamp}.css`,
chunkFilename: `${getAssetsPath()}css/[name].${Timestamp}.css`,
},
},
};
2.6 router.ts
路由配置支持 hash 和 history 模式切换:
import { createRouter, createWebHashHistory, createWebHistory } from "vue-router";
const router = createRouter({
history: process.env.VUE_APP_ROUTER_MODE === 'hash'
? createWebHashHistory(process.env.BASE_URL)
: createWebHistory(process.env.BASE_URL),
routes,
});
router.beforeResolve((to, from, next) => {
const isFileProtocol = window.location.protocol === "file:";
let token = localStorage.getItem("token");
if (!token) {
if (isFileProtocol) {
next("./official_website?redirect=" + encodeURIComponent(to.path));
} else {
next("/official_website?redirect=" + encodeURIComponent(to.path));
}
} else {
next();
}
});
3. 打包流程
安装依赖:
npm install
启动开发模式:
npm run electron:serve
打包 Electron 应用:
npm run electron:build
生成的安装包会存放在 release 文件夹中。
4. 总结
通过上述步骤,您可以成功将 Vue3.2 项目打包为 Electron 桌面应用。Electron 提供了强大的跨平台能力,而 Vue 则让前端开发更加高效。希望本文对您有所帮助!