Electron 的渲染进程本质上就是一个可以运行任意前端框架的浏览器环境。从技术上讲,你完全可以直接在 index.html 里用 CDN 引入 Vue 或 React 的 UMD 文件开始写界面,但在实际项目中,这么做很快会遭遇组件化开发、热更新、状态管理、路由等工程化的需求。这一节会告诉你如何将主流前端框架“真正”地整合进 Electron 项目,并且是生产可用的方式。
8.3.1 整合的基本思路
无论你使用哪种框架,整合的核心目标都是让开发体验和普通的 Web 单页应用一样,同时保证 Electron 的安全模型不被破坏。最典型的架构模式如下:
- 用一个前端构建工具(Vite、Webpack 等)单独管理全部渲染进程代码。
- 开发时,构建工具启动一个 dev server(如
localhost:5173),Electron 窗口加载这个地址,享受模块热替换(HMR)和极快的刷新速度。 - 生产构建时,先将前端代码打包成静态文件(
dist/index.html+ JS/CSS),再由 Electron 的win.loadFile加载本地文件。 - 主进程代码单独编写,不需要被前端构建工具处理(或用
electron-vite等工具一同管理)。
这种“前后端分离”的架构,跟 Electron 主进程 / 渲染进程的划分天然契合。
8.3.2 使用脚手架快速起步
手动配置 Webpack 和 Babel 太过繁琐,社区提供了若干成熟的脚手架来降低门槛:
- electron-vite(推荐)
基于 Vite,一体式管理主进程、预加载脚本和渲染进程。它直接支持 Vue、React、Svelte 等模板,开箱即用地解决了 HMR、路径别名、环境变量注入等问题。
- electron-forge + Vite 模板
Electron 官方维护的 forge 工具也提供了基于 Vite 的模板,选择时添加 --template=vite 参数即可,之后同样可以集成 Vue 或 React。
- electron-react-boilerplate 或 electron-vue-template
针对特定框架的社区模板,历史较长,但部分可能依赖老旧工具链(如 Babel),选择时注意看维护状态。
如果是从零开始的真实项目,我比较推荐 electron-vite。它的目录结构极其清晰:
my-app/
├── electron.vite.config.js
├── src/
│ ├── main/ # 主进程代码
│ ├── preload/ # 预加载脚本
│ └── renderer/ # 渲染进程(Vue/React 项目)
│ ├── index.html
│ └── src/
└── package.json
你只需要在初始化时选择框架,就能直接开始写业务代码。
8.3.3 手工集成 Vue 3 的例子
假设你想在现有 Electron 项目里手工加上 Vue 3 + Vite,可以按以下步骤操作(同样适用于 React,仅构建细节不同)。
1. 安装依赖
npm install vue
npm install -D vite @vitejs/plugin-vue
2. 创建 Vite 配置(项目根目录下 vite.config.js)
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
root: 'src/renderer', // 渲染进程源码目录
base: './', // 确保资源路径使用相对路径
build: {
outDir: '../../dist/renderer',
emptyOutDir: true,
},
server: {
port: 5173,
},
})
3. 在 src/renderer 里搭建 Vue 应用
src/renderer/
├── index.html
├── src/
│ ├── main.js
│ └── App.vue
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>My App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="./src/main.js"></script>
</body>
</html>
src/main.js
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
src/App.vue
<template>
<div>
<h1>Hello from Vue 3!</h1>
<button @click="sendMsg">Send to Main</button>
</div>
</template>
<script setup>
import { ipcRenderer } from 'electron' // 注意安全,如是预加载则从 contextBridge 暴露的 api 调用
function sendMsg() {
// 此处仅供示例,真实代码应在 preload 中安全暴露
window.api?.send('toMain', 'Hello')
}
</script>
4. 在主进程中根据环境加载不同内容
// main.js 主进程
const win = new BrowserWindow({
// ...
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
})
if (process.env.VITE_DEV_SERVER_URL) {
// 开发模式,加载 Vite 服务器
win.loadURL(process.env.VITE_DEV_SERVER_URL)
} else {
// 生产模式,加载打包后的文件
win.loadFile(path.join(__dirname, '../dist/renderer/index.html'))
}
VITE_DEV_SERVER_URL 这个环境变量可以由 electron-vite 自动注入,如果是手工配置,可以在开发脚本里用 cross-env 设置,或者自己写一小段判断代码去找 Vite 的端口。
8.3.4 必须注意的安全实践
整合前端框架时最容易被忽视的就是安全。由于渲染进程天然可以运行任意 JavaScript,你绝对不能图一时方便而启用 nodeIntegration: true 并把 contextIsolation 关掉。
现代 Electron 的安全底线是:
contextIsolation: true(默认)nodeIntegration: false(默认)- 通过
preload脚本 +contextBridge精确暴露给渲染进程有限的 API。
也就是说,你的 Vue/React 组件中不应该能直接 require('fs') 或 require('electron')。如果需要从渲染进程调用主进程能力,应该这样做:
// preload.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('api', {
openFile: () => ipcRenderer.invoke('dialog:openFile'),
// ... 其他白名单方法
})
然后在组件里调用 window.api.openFile()。这种封装方式不仅安全,还能让你直观地看见应用暴露了哪些系统能力,方便审计和测试。
8.3.5 开发时的热更新与调试
将前端框架的运行环境完全交给了 Vite 后,所有的 HMR、快速刷新、错误遮罩层都是即开即用。你只需在启动脚本中先启动 Vite dev server,然后再起 Electron:
"scripts": {
"dev": "concurrently \"vite\" \"wait-on http://localhost:5173 && electron .\""
}
这样修改 Vue/React 组件时,Electron 窗口会立刻反映变化,和浏览器开发别无二致。此外,你依然可以在 Electron 窗口内使用 DevTools(Ctrl+Shift+I),并安装 Vue DevTools 或 React DevTools 的浏览器扩展,调试组件树和状态。
// 仅在开发模式时自动打开 DevTools
if (process.env.NODE_ENV === 'development') {
win.webContents.openDevTools()
}
8.3.6 路由与多窗口
使用前端路由(vue-router、react-router)时,注意 Electron 加载的是一个本地文件,默认的 history 模式会依赖服务器配置,所以最稳妥的方式是使用 hash 模式。
// vue-router
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [...]
})
如果你需要在应用中创建多个窗口(比如设置页、浮动工具窗口),每个窗口都应该拥有自己独立的渲染进程和 HTML 入口。你可以在主进程中为不同入口准备不同的 BrowserWindow,并分别加载二次打包后的页面。通常的实践是:主窗口加载 renderer/index.html,设置窗口加载 renderer/settings.html,并且都由同一套 Vite 构建输出两个 HTML 文件。
// vite.config.js 多入口
build: {
rollupOptions: {
input: {
main: resolve(__dirname, 'src/renderer/index.html'),
settings: resolve(__dirname, 'src/renderer/settings/index.html'),
}
}
}
这样 Electron 的应用能力就与前端框架的工程化结构无缝结合了。
8.3.7 真实项目的目录结构推荐
结合前面所有内容,一个生产级 Electron 项目整合前端框架后的目录可能是这样的:
my-electron-app/
├── package.json
├── electron.vite.config.js
├── src/
│ ├── main/ # 主进程
│ │ ├── index.js # 入口,创建窗口等
│ │ └── ipc-handlers.js # IPC 通信逻辑
│ ├── preload/ # 预加载脚本
│ │ └── index.js
│ └── renderer/ # Vue 或 React 前端项目
│ ├── index.html
│ ├── settings.html
│ └── src/
│ ├── main.js
│ ├── App.vue
│ ├── router/
│ ├── stores/
│ └── components/
├── resources/ # 图标等静态资源
└── build/ # 打包配置 (electron-builder)
这个结构清晰划分了三个主要部分,任何新同事都能快速看懂并上手修改。
前端框架与 Electron 的整合绝非简单地塞入一个 <script> 标签。它需要你正确搭建构建工具链、坚守安全隔离原则,并为多窗口、路由等桌面特有需求做出适配。一旦完成这一步,你的 Electron 应用就真正拥有了 Web 世界里最先进的开发体验,等于用与纯前端项目几乎相同的代码结构和迭代速度,来交付一个带有完整系统权限的桌面程序。这,正是 Electron 赋予开发者的最强大生产力之一。