使用 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.jsonbuild,增加后端文件配置项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,发给别人就能一键安装运行项目了。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐