9b60807cca9d62b20ffee90b74daf88254f6ab71
Electron + Vue3 + TypeScript 项目
基于 electron-vite 构建的现代化 Electron 应用模板。
技术栈
| 领域 | 技术 |
|---|---|
| 构建工具 | electron-vite |
| 前端框架 | Vue 3 (Composition API) |
| 语言 | TypeScript |
| UI | 原生 HTML/CSS + Vue 组件 |
项目结构
├── electron.vite.config.ts # Vite 配置(electron-vite 专用)
├── tsconfig*.json # TypeScript 配置
├── src/
│ ├── main/ # 主进程(Node.js 环境)
│ │ └── index.ts
│ ├── preload/ # 预加载脚本(桥接层)
│ │ ├── index.ts
│ │ └── index.d.ts # 类型声明
│ └── renderer/ # 渲染进程(浏览器环境)
│ ├── index.html
│ └── src/
│ ├── main.ts # Vue 入口
│ ├── App.vue
│ └── components/
│ └── Counter.vue
├── out/ # 构建输出目录
└── package.json
核心概念
1. 三进程架构
Electron 有三个独立的进程:
┌─────────────────────────────────────────────────┐
│ Main Process │
│ (Node.js) - 创建窗口、管理应用生命周期 │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────┐ │
│ │ Preload Script │ │
│ │ (上下文桥接,安全暴露 API 到渲染进程) │ │
│ └───────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────┐ │
│ │ Renderer Process │ │
│ │ (Chromium) - Vue 应用运行,DOM 操作 │ │
│ └───────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
主进程 (main):运行 Node.js,负责窗口管理、应用生命周期、系统 API 调用
预加载脚本 (preload):运行在渲染进程中,但能访问 Node.js API,通过 contextBridge 安全暴露有限 API
渲染进程 (renderer):运行 Web 内容,Vue/React 应用在此执行,只能通过 IPC 与主进程通信
2. 上下文隔离 (Context Isolation)
// main.js - 窗口配置
webPreferences: {
contextIsolation: true, // 启用上下文隔离
nodeIntegration: false, // 禁用 Node.js 访问
preload: 'preload.js' // 加载预加载脚本
}
contextIsolation: true防止渲染进程直接访问 Node.js,防止 XSS 攻击- 渲染进程只能通过
window.electron或window.api访问预暴露的 API
3. IPC 通信模式
Renderer Main
│ │
│ ipcRenderer.invoke('xxx') │
│ ─────────────────────────────►│
│ │ ipcMain.handle('xxx', ...)
│ Promise<Result> │
│ ◄─────────────────────────────│
▼ ▼
// preload - 暴露 IPC 通道
contextBridge.exposeInMainWorld('api', {
getData: () => ipcRenderer.invoke('get-data')
})
// renderer - 调用
const data = await window.api.getData()
// main - 处理
ipcMain.handle('get-data', () => {
return { message: 'Hello from main!' }
})
4. electron-vite 构建流程
┌─────────────────────────────────────────────────────────┐
│ electron-vite dev │
├─────────────────────────────────────────────────────────┤
│ main │ Vite (SSR) → out/main/index.js │
│ preload │ Vite (ESM) → out/preload/index.mjs │
│ renderer│ Vite + Vue → http://localhost:5173 │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ electron-vite build │
├─────────────────────────────────────────────────────────┤
│ main │ Vite (SSR) → out/main/index.js │
│ preload │ Vite (ESM) → out/preload/index.mjs │
│ renderer│ Vite + Vue → out/renderer/ │
└─────────────────────────────────────────────────────────┘
┌─────────────┬─────────────────────────────────────────┐
│ 旧文件 │ → 新文件 │
├─────────────┼─────────────────────────────────────────┤
│ main.js │ src/main/index.ts │
├─────────────┼─────────────────────────────────────────┤
│ preload.js │ src/preload/index.ts │
├─────────────┼─────────────────────────────────────────┤
│ index.html │ src/renderer/index.html │
├─────────────┼─────────────────────────────────────────┤
│ renderer.js │ src/renderer/src/components/Counter.vue │
└─────────────┴─────────────────────────────────────────┘
electron-vite 自动处理:
- 主进程和预加载脚本的 CommonJS/ESM 兼容
- 渲染进程的热模块替换 (HMR)
- 路径别名、依赖打包等
5. Vue 组件生命周期
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
const count = ref(0)
let timer: ReturnType<typeof setTimeout> | null = null
function updateCounter(): void {
count.value++
timer = setTimeout(updateCounter, 1000)
}
onMounted(() => {
updateCounter()
})
onUnmounted(() => {
if (timer) clearTimeout(timer)
})
</script>
onMounted- 组件挂载时启动定时器onUnmounted- 组件卸载时清理定时器,防止内存泄漏
命令
# 开发模式(热重载)
npm run dev
# 生产构建
npm run build
# 运行已构建的应用
npm start
# 打包为可执行文件
npm run package
开发建议
- IPC 优先:渲染进程不直接操作系统 API,通过 IPC 委托给主进程
- 类型安全:preload 的 API 声明在
.d.ts文件中,renderer 可获得智能提示 - 组件化:UI 逻辑封装在 Vue 组件中,主进程只负责业务无关的系统操作
- 清理资源:组件卸载时务必清理定时器、事件监听器等
TypeScript 配置
项目有 3 个 TypeScript 配置文件:
tsconfig.json(根配置/入口)
{
"references": [
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.web.json" }
]
}
作用:项目总入口,本身不编译代码,通过 references 引用其他两个配置。
tsconfig.node.json(Node.js 环境)
对应:src/main/ + src/preload/ + electron.vite.config.ts
{
"lib": ["ES2022"],
"include": ["src/main/**/*", "src/preload/**/*", "electron.vite/**/*"]
}
作用:为主进程和预加载脚本提供类型检查,这两个运行在 Node.js 环境,需要 Node.js 内置类型(如 process、path、fs)。
tsconfig.web.json(浏览器环境)
对应:src/renderer/
{
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"jsx": "preserve",
"jsxImportSource": "vue",
"include": ["src/renderer/**/*"]
}
作用:为渲染进程(Vue 应用)提供类型检查,需要 DOM 类型(如 window、document、HTMLElement)和 Vue JSX 支持。
为什么分开?
┌─────────────────┐
│ tsconfig.json │ ← 根配置,定义整体规则
└────────┬────────┘
│
references
│
┌────┴────┐
▼ ▼
┌───────┐ ┌───────┐
│ node │ │ web │ ← 分别针对不同运行环境
│ .json │ │ .json │
└───┬───┘ └───┬───┘
│ │
Node.js 浏览器
API 类型 DOM 类型
不同运行环境有不同的全局 API,分开配置可以:
- 避免类型冲突(Node.js 的
globalvs 浏览器的window) - 按需引入类型库,减小检查范围
- IDE 根据当前文件路径自动选择对应配置
全部文件清单
| 文件 | 作用 |
|---|---|
package.json |
项目配置:依赖、脚本、electron-builder 设置 |
electron.vite.config.ts |
构建入口:定义三个进程的入口路径、插件、别名 |
tsconfig.json |
TS 总入口,引用其他两个配置 |
tsconfig.node.json |
Node.js 环境配置(主进程、预加载) |
tsconfig.web.json |
浏览器环境配置(渲染进程、Vue) |
src/main/index.ts |
Electron 主进程:创建窗口、管理应用生命周期、处理 IPC |
src/preload/index.ts |
预加载脚本:通过 contextBridge 暴露安全 API |
src/preload/index.d.ts |
预加载 API 的 TypeScript 类型声明 |
src/renderer/index.html |
渲染进程 HTML 入口 |
src/renderer/src/main.ts |
Vue 应用入口 |
src/renderer/src/App.vue |
Vue 根组件 |
src/renderer/src/components/Counter.vue |
计数器业务组件 |
out/ |
构建输出目录(.gitignore 忽略) |
electron.vite.config.ts 详解
这是 electron-vite 的核心配置文件,作用类似普通 Vite 项目的 vite.config.ts,但需要为三个进程分别配置:
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
// ── 主进程配置 ──
main: {
plugins: [externalizeDepsPlugin()], // 依赖外置,不打包进输出
build: {
rollupOptions: {
input: { index: 'src/main/index.ts' }
}
}
},
// ── 预加载脚本配置 ──
preload: {
plugins: [externalizeDepsPlugin()],
build: {
rollupOptions: {
input: { index: 'src/preload/index.ts' }
}
}
},
// ── 渲染进程配置 ──
renderer: {
root: 'src/renderer', // HTML 所在目录
plugins: [vue()], // Vue 插件
resolve: {
alias: { '@': 'src/renderer/src' } // 路径别名
},
build: {
rollupOptions: {
input: 'src/renderer/index.html' // HTML 入口
}
}
}
})
关键点
| 配置项 | 作用 |
|---|---|
externalizeDepsPlugin() |
将 node_modules 中的依赖转为 require() 引用,减小包体积 |
root: 'src/renderer' |
告诉 Vite 渲染进程的 HTML 在哪个目录 |
plugins: [vue()] |
在渲染进程启用 Vue 插件,处理 .vue 文件 |
resolve.alias |
设置 @ 指向 src/renderer/src,简化导入路径 |
build.rollupOptions.input |
指定每个进程的入口文件 |
构建后输出
electron-vite build
│
▼
┌──────────────────────────────────────────┐
│ out/main/index.js (主进程) │
│ out/preload/index.mjs (预加载) │
│ out/renderer/index.html (渲染进程) │
│ out/renderer/assets/* (JS/CSS) │
└──────────────────────────────────────────┘
Description
Languages
TypeScript
58.4%
Vue
28.2%
HTML
13.4%