人人都会AI编程

18.1 Vite + Electron 构建方案:双进程构建、路径适配

更新时间:2026-07-11

将 Vite 引入 Electron 项目,本质上是把“前端开发”和“桌面主进程开发”这两个原本独立的流程整合到一套构建体系里。Vite 极快的冷启动与模块热替换(HMR)能极大改善渲染进程的开发体验,但 Electron 的主进程是一个 Node.js 环境,它不能直接使用浏览器端的 ESM 模块,因此需要分别处理。这就引出了本节的核心设计:双进程分别构建、开发与生产环境路径自动适配

18.1.1 为什么需要双进程构建

Electron 应用包含三类代码:

  • 渲染进程 —— 就是前端页面,可以使用 Vue、React 等框架,希望享受 Vite 的 HMR 和按需编译。
  • 主进程 —— 运行在 Node.js 环境,负责窗口管理、系统调用,通常用 CommonJS 或打包成 CJS 格式。虽然 Node.js 也支持 ESM,但 Electron 的主进程入口文件一般仍以 CommonJS 为主,且需要处理 __dirname 等 Node 全局变量。
  • 预加载脚本 —— 在渲染进程的沙盒环境中运行,同样需要以 CommonJS 或 ESM 形式输出,且环境介于两者之间。

这三类代码的构建目标不同:渲染进程输出纯浏览器可运行的静态资源(HTML/CSS/JS),主进程和预加载脚本输出 Node.js 可执行的 JavaScript 文件。如果强行用一套配置去处理,会出现模块解析冲突、环境变量错乱等问题。

双进程构建的思路就是:为渲染进程和主进程/预加载脚本分别准备独立的 Vite 配置文件(或借助 electron-vite 等工具),在 vite build 时分别输出到不同的目录,然后在开发模式下用 Vite Dev Server 提供渲染进程的 HMR 服务,主进程则直接用 electron . 启动并加载 Dev Server 地址。

18.1.2 渲染进程:标准 Vite 项目

渲染进程的配置与普通前端项目几乎一致,使用 Vite 的默认模式即可。你可以直接利用 create-vite 创建一个 Vue 或 React 模板,然后稍作调整。

// vite.renderer.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [vue()],
  root: 'src/renderer',      // 渲染进程源码目录
  base: './',                // 使用相对路径,避免打包后资源 404
  build: {
    outDir: '../../dist/renderer',  // 输出目录,供主进程加载
    emptyOutDir: true,
  },
});

开发时启动 Vite Dev Server:

vite --config vite.renderer.config.js

随后你会得到一个开发服务器地址,例如 http://localhost:5173。在开发模式下,主进程直接加载这个 URL,所有前端资源在内存中编译,修改代码即可热更新窗口内容。

18.1.3 主进程与预加载脚本:Node 环境构建

主进程和预加载脚本需要被打包成能在 Node.js 中运行的代码。Vite 本身具备 SSR/Node 构建能力,但更常用的做法是使用 vitebuild 命令,目标设为 node 或直接使用 esbuild/tsc 处理。核心要求:

  • 输出格式为 CommonJSformat: 'cjs'),因为 Electron 主进程入口需要 require
  • 不能将 electron 自身或 Node 内置模块打包进去,必须设置为 external
  • 保留 dirnamefilename 的原始行为(Vite 默认会转换 ESM,需要关闭或特殊配置)。

一个简化版的主进程 Vite 配置:

// vite.main.config.js
import { defineConfig } from 'vite';
import { builtinModules } from 'module';

export default defineConfig({
  build: {
    outDir: 'dist/main',
    lib: {
      entry: 'src/main/index.js',   // 主进程入口
      formats: ['cjs'],
      fileName: () => 'index.js',
    },
    rollupOptions: {
      external: ['electron', ...builtinModules], // 不要把 Node 内置模块和 electron 打包
    },
    minify: false,  // 主进程通常不压缩,便于调试
  },
});

预加载脚本的构建配置类似,也可以共用一个配置,通过多入口输出:

build: {
  rollupOptions: {
    input: {
      main: 'src/main/index.js',
      preload: 'src/main/preload.js',
    },
    output: {
      format: 'cjs',
      entryFileNames: '[name].js',
    },
  },
}

18.1.4 路径适配:开发模式与生产模式的智慧切换

双进程构建面临的真正挑战在于,开发和生产环境下,主进程加载渲染进程的方式完全不同:

  • 开发模式:渲染进程由 Vite Dev Server 提供,URL 形如 http://localhost:5173
  • 生产模式:渲染进程被打包成静态文件,位于 dist/renderer/index.html,通过 file:// 协议或 Custom Protocol 加载。

为了让主进程在两种环境下都能正确加载页面,通常用一个环境变量来区分,并在 BrowserWindow 创建时动态判断。

// src/main/index.js
const { app, BrowserWindow } = require('electron');
const path = require('path');

const isDev = process.env.NODE_ENV === 'development';
const VITE_DEV_SERVER_URL = process.env.VITE_DEV_SERVER_URL; // 启动时传入

function createWindow() {
  const win = new BrowserWindow({
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false,
    },
  });

  if (isDev && VITE_DEV_SERVER_URL) {
    win.loadURL(VITE_DEV_SERVER_URL);
    win.webContents.openDevTools();
  } else {
    win.loadFile(path.join(__dirname, '../renderer/index.html'));
  }
}

app.whenReady().then(createWindow);

这里 __dirname 最终指向 dist/main/,所以 ../renderer/index.html 正好能定位到渲染进程的构建输出目录。保持目录结构清晰是避免路径问题的关键。

在实际项目中,你可以通过 cross-env 和 npm scripts 来传递变量:

{
  "scripts": {
    "dev:renderer": "vite --config vite.renderer.config.js",
    "dev:main": "cross-env NODE_ENV=development VITE_DEV_SERVER_URL=http://localhost:5173 electron .",
    "dev": "concurrently \"npm run dev:renderer\" \"npm run dev:main\""
  }
}

开发时只需要 npm run dev,就能同时启动 Vite 和 Electron,窗口自动打开并加载开发服务器内容。

18.1.5 使用 electron-vite 简化构建

如果你不想手动维护多个 Vite 配置文件,社区提供了 electron-vite 这个一体化方案。它将主进程、预加载脚本和渲染进程的构建统一在一个配置文件内,通过约定目录结构自动处理双进程构建和路径注入。

安装后,项目结构天然分离:

├── electron.vite.config.js
├── src
│   ├── main        # 主进程
│   ├── preload     # 预加载
│   └── renderer    # 渲染进程(前端项目)

配置文件类似:

import { defineConfig } from 'electron-vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  main: {
    build: { rollupOptions: { external: ['electron'] } }
  },
  preload: {
    build: { rollupOptions: { external: ['electron'] } }
  },
  renderer: {
    plugins: [vue()]
  }
});

它自动处理了 __dirnameprocess.env.ELECTRON_RENDERER_URL 等变量,开发时自动注入正确的 URL,生产时自动指向 out/renderer/index.html。对于追求快速启动的项目,electron-vite 是一个极其实用的选择。

18.1.6 常见坑与真实建议

  • 路径问题:务必确认 loadFile 的路径基于 __dirnameapp.getAppPath(),不要写成相对当前工作目录的字符串,否则打包后运行会出错。
  • 资源路径:渲染进程中如果使用了动态图片或字体,确保 base 配置正确,生产模式下通过 import 引入的资源会被自动处理,但如果使用绝对路径 /assets/logo.png,在 file:// 协议下会失效。建议统一使用相对路径或显式 import
  • C++ 原生模块:若主进程依赖 better-sqlite3 等原生模块,必须保证它们被 external 且正确 require,不要尝试用 Vite 打包 .node 文件,通常需要额外用 electron-rebuild 处理。
  • HMR 与窗口状态:修改主进程代码后不能热更新,需要重启应用。可以借助 electron-reloadnodemon 监听主进程文件变更自动重启。渲染进程的 HMR 完全保留,效率极高。

通过双进程构建和路径适配,Vite 的极速开发体验被无缝嫁接到了 Electron 桌面开发中。这意味着你既保留了前端领域最先进的工具链,又能触达操作系统的每一寸能力,真正实现了“桌面应用前端化”的工程梦想。