将 Vue 3 + Vite + TS 项目打包为 .exe 文件
使用 electron-builder打包:
一、安装依赖
npm install electron electron-builder --save-dev
如果安装时出现网络错误,就使用镜像安装
在项目根目录下新建.npmrc 文件,添加以下内容:
electron_mirror=https://npmmirror.com/mirrors/electron/
electron_headers_mirror=https://npmmirror.com/mirrors/electron-headers/
然后再次执行npm install electron electron-builder --save-dev即可安装成功!
二、创建 Electron 入口文件
在项目根目录下创建 electron/main.js 文件,如果你的 package.json 包含 "type": "module",则用ESM的import语法,也就是如下代码,如果没有"type": “module”,则要用CommonJS的require语法,例如:const { app, BrowserWindow } = require('electron')
import { app, BrowserWindow } from 'electron';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
let win;
function createWindow() {
win = new BrowserWindow({
width: 1920,
height: 1080,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
});
// 开发环境下加载 Vite 开发服务器
if (process.env.NODE_ENV === 'development') {
win.loadURL('http://localhost:5173');
} else {
// 生产环境下加载打包后的文件
win.loadFile(path.join(__dirname, '../dist/index.html'));
}
// win.webContents.openDevTools(); //如果要打开F12,则写这句
}
app.whenReady().then(createWindow);
// 所有窗口关闭时退出应用(macOS 除外)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
// macOS 下点击 Dock 图标重新打开窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindows();
});
三、在项目根目录新增electron.vite.config.ts文件
通过这个入口文件打包,安装包体积会减少很多:
import baseConfig from "./vite.config";
import { defineConfig, loadEnv } from "vite";
import electron from "vite-plugin-electron";
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), "");
return {
...baseConfig,
plugins: baseConfig.plugins?.concat([
electron({
entry: "electron/main.js", // 主进程
onstart(args) {
if (process.env.NODE_ENV === "development") {
args.startup([".", "--no-sandbox", "--remote-debugging-port=9222"]);
}
},
vite: {
build: {
outDir: "dist", // 跟渲染进程同一个目录
emptyOutDir: false,
rollupOptions: {
input: {
main: "electron/main.js",
},
output: {
format: "cjs",
entryFileNames: "[name].js",
},
},
},
},
}),
]),
};
});
四、修改 package.json
添加/修改以下配置:
{
"main": "electron/main.js",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview",
"electron:serve": "vite build && electron .",
"build:electron": "vite build --config electron.vite.config.ts && electron-builder"
},
"build": {
"productName": "main",
"appId": "com.example.app",
"files": [
"dist/**/*",
"electron/main.js",
"public/**/*",
"package.json"
],
"directories": {
"output": "dist"
}
},
五、构建应用
npm run build:electron
可能会遇到的问题:
1、打包过程中出现下载资源失败:
(1)可以使用淘宝镜像下载
(2)可以直接复制下载失败的链接,然后手动下载后,粘贴到电脑C盘%LOCALAPPDATA%\electron-builder\Cache路径下
2、打包后点击.exe出现白屏或黑屏:
这里一定要改路由模式为hash模式,我之前写的是createWebHistory打包后是黑屏,改成createWebHashHistory就好了

3、如果前端有多个页面显示,页面之间没有点击按钮的跳转,是通过改地址栏路由的方式打开的,那么electron打包后点击,exe只会显示首页,又不像浏览器一样可以改地址栏怎么办:
修改electron/main.js文件,在main.js 中启动两个窗口,分别加载两个地址就可以实现,点击.exe同时打开两个窗口,以下是修改的代码
import { app, BrowserWindow } from 'electron';
import path from 'path';
import { fileURLToPath } from 'url';
// 兼容 ESM 的 __dirname
const __dirname = path.dirname(fileURLToPath(import.meta.url));
let mainWin; // 页面1
let faultWin; // 页面2
async function createWindows() {
//页面1
mainWin = new BrowserWindow({
width: 1920,
height: 1080,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
});
//页面2
faultWin = new BrowserWindow({
width: 1920,
height: 1080,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
});
// 加载页面
if (process.env.NODE_ENV === 'development') {
await mainWin.loadURL('http://localhost:3000');
await faultWin.loadURL('http://localhost:3000/#/fault-recognition');
} else {
await mainWin.loadFile(path.join(__dirname, '../dist/index.html'));
await faultWin.loadFile(path.join(__dirname, '../dist/index.html'), {
hash: '#/fault-recognition',
});
}
// 可选:开发时自动打开 DevTools(可以去掉)
mainWin.webContents.openDevTools();
faultWin.webContents.openDevTools();
}
// 启动应用
app.whenReady().then(createWindows);
// 所有窗口关闭时退出应用(macOS 除外)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
// macOS 下点击 Dock 图标重新打开窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindows();
});
扩展:
如何将前端和后端项目打包为一个.exe文件,用户双击一个文件就能运行项目呢?
1、首先把后端打包成.exe文件,另外一篇文章有教程:https://blog.csdn.net/qdm13209211861/article/details/149395026?spm=1001.2014.3001.5501
2、然后将后端的.exe文件和data静态资源文件,放在backend文件夹下,将backend文件夹放在前端项目的根目录
3、修改Electron 主进程文件 main.js:
import { exec } from "child_process";
import { app, BrowserWindow } from "electron";
import path from "path";
import { fileURLToPath } from "url";
import fs from "fs";
import net from "net";
// ESM 兼容 __dirname
const __dirname = path.dirname(fileURLToPath(import.meta.url));
let pythonProcess;
let mainWin;
// 启动后端
function startPythonBackend() {
const pythonPath =
process.env.NODE_ENV === "development"
? path.resolve(__dirname, "../backend/main.exe") // 开发环境:electron/../backend/main.exe
: path.resolve(process.resourcesPath, "backend/main.exe"); // 生产环境:resources/backend/main.exe
// 核心校验:文件是否存在
if (!fs.existsSync(pythonPath)) {
console.error("后端文件不存在:", pythonPath);
return;
}
pythonProcess = exec(
`"${pythonPath}"`,
{
stdio: "inherit",
cwd: path.dirname(pythonPath),
shell: true,
},
(err) => {
err
? console.error("后端启动失败:", err.message)
: console.log("后端启动成功");
}
);
pythonProcess.on("exit", (code) => {
console.log(`🔌 后端退出,码:${code}`);
pythonProcess = null;
});
}
// 创建窗口
async function createWindows() {
mainWin = new BrowserWindow({
width: 1920,
height: 1080,
useContentSize: true,
icon: path.resolve(process.resourcesPath, "img/logo.png"),
autoHideMenuBar: true,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
webSecurity: process.env.NODE_ENV === "development", // 仅开发环境关闭跨域
},
});
if (process.env.NODE_ENV === "development") {
await mainWin.loadURL("http://localhost:3000");
mainWin.webContents.openDevTools();
} else {
const indexPath = path.resolve(__dirname, "../dist/index.html");
fs.existsSync(indexPath)
? await mainWin.loadFile(indexPath)
: console.error("前端文件缺失:", indexPath);
}
}
// 等待端口就绪
function waitPortReady(port, host = "127.0.0.1", timeout = 5000) {
return new Promise((resolve, reject) => {
const start = Date.now();
(function check() {
const socket = net.createConnection(port, host);
socket.on("connect", () => {
socket.end();
resolve();
});
socket.on("error", () => {
if (Date.now() - start > timeout) return reject("端口启动超时");
setTimeout(check, 100);
});
})();
});
}
// 核心启动
app.whenReady().then(async () => {
startPythonBackend();
try {
await waitPortReady(8081, "127.0.0.1", 5000); // 等后端监听端口
await createWindows(); // 端口就绪后再创建窗口
} catch (err) {
console.error("后端启动失败或超时", err);
await createWindows(); // 即使后端失败,也显示窗口
}
});
// 窗口关闭逻辑
app.on("window-all-closed", () => process.platform !== "darwin" && app.quit());
// macOS 激活逻辑
app.on("activate", () => {
if (!BrowserWindow.getAllWindows().length)
createWindows().then(() => !pythonProcess && startPythonBackend());
});
// 退出时终止后端
app.on("before-quit", () => {
pythonProcess && (pythonProcess.kill(), console.log("后端已终止"));
});
4、修改package.json的build,增加后端文件配置项extraResources:
"build": {
"productName": "main",
"appId": "com.example.app",
"files": [
"dist/**/*",
"electron/main.js",
"public/**/*",
"package.json"
],
"directories": {
"output": "dist"
},
"extraResources": [ //额外资源文件夹,不会被打包到 asar 里,而是直接复制到安装目录
{
"from": "backend",
"to": "backend"
},
{
"from": "public/img",
"to": "img"
}
],
"win": {
"target": "nsis",
"asar": true
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"installerIcon": "public/img/logo.ico",
"uninstallerIcon": "public/img/logo.ico",
"shortcutName": "风险评估"
}
},
5、将前端打包时访问的接口地址改为后端.exe启动的地址,我这里是http://localhost:8081/
6、打包项目:npm run build:electron
7、双击前端的dist/win-unpacked/main.exe文件,就可以一键启动项目了;安装包是main Setup 0.0.0.exe,发给别人就能一键安装运行项目了。
更多推荐

所有评论(0)