将 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 构建能力,但更常用的做法是使用 vite 的 build 命令,目标设为 node 或直接使用 esbuild/tsc 处理。核心要求:
- 输出格式为 CommonJS(
format: 'cjs'),因为 Electron 主进程入口需要require。 - 不能将
electron自身或 Node 内置模块打包进去,必须设置为external。 - 保留
dirname和filename的原始行为(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()]
}
});
它自动处理了 __dirname、process.env.ELECTRON_RENDERER_URL 等变量,开发时自动注入正确的 URL,生产时自动指向 out/renderer/index.html。对于追求快速启动的项目,electron-vite 是一个极其实用的选择。
18.1.6 常见坑与真实建议
- 路径问题:务必确认
loadFile的路径基于__dirname或app.getAppPath(),不要写成相对当前工作目录的字符串,否则打包后运行会出错。 - 资源路径:渲染进程中如果使用了动态图片或字体,确保
base配置正确,生产模式下通过import引入的资源会被自动处理,但如果使用绝对路径/assets/logo.png,在file://协议下会失效。建议统一使用相对路径或显式import。 - C++ 原生模块:若主进程依赖
better-sqlite3等原生模块,必须保证它们被external且正确require,不要尝试用 Vite 打包.node文件,通常需要额外用 electron-rebuild 处理。 - HMR 与窗口状态:修改主进程代码后不能热更新,需要重启应用。可以借助
electron-reload或nodemon监听主进程文件变更自动重启。渲染进程的 HMR 完全保留,效率极高。
通过双进程构建和路径适配,Vite 的极速开发体验被无缝嫁接到了 Electron 桌面开发中。这意味着你既保留了前端领域最先进的工具链,又能触达操作系统的每一寸能力,真正实现了“桌面应用前端化”的工程梦想。