This commit is contained in:
2026-05-21 00:30:13 +08:00
parent 18a14111f8
commit 2e8ee357d5
+332
View File
@@ -0,0 +1,332 @@
# Electron + Vue3 + TypeScript 项目
基于 [electron-vite](https://electron-vite.org/) 构建的现代化 Electron 应用模板。
## 技术栈
| 领域 | 技术 |
|------|------|
| 构建工具 | [electron-vite](https://electron-vite.org/) |
| 前端框架 | 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)
```javascript
// 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> │
│ ◄─────────────────────────────│
▼ ▼
```
```typescript
// 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/ │
└─────────────────────────────────────────────────────────┘
```
electron-vite 自动处理:
- 主进程和预加载脚本的 CommonJS/ESM 兼容
- 渲染进程的热模块替换 (HMR)
- 路径别名、依赖打包等
### 5. Vue 组件生命周期
```typescript
<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` - 组件卸载时清理定时器,防止内存泄漏
## 命令
```bash
# 开发模式(热重载)
npm run dev
# 生产构建
npm run build
# 运行已构建的应用
npm start
# 打包为可执行文件
npm run package
```
## 开发建议
1. **IPC 优先**:渲染进程不直接操作系统 API,通过 IPC 委托给主进程
2. **类型安全**preload 的 API 声明在 `.d.ts` 文件中,renderer 可获得智能提示
3. **组件化**:UI 逻辑封装在 Vue 组件中,主进程只负责业务无关的系统操作
4. **清理资源**:组件卸载时务必清理定时器、事件监听器等
## TypeScript 配置
项目有 3 个 TypeScript 配置文件:
### tsconfig.json(根配置/入口)
```json
{
"references": [
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.web.json" }
]
}
```
**作用**:项目总入口,本身不编译代码,通过 `references` 引用其他两个配置。
### tsconfig.node.jsonNode.js 环境)
**对应**`src/main/` + `src/preload/` + `electron.vite.config.ts`
```json
{
"lib": ["ES2022"],
"include": ["src/main/**/*", "src/preload/**/*", "electron.vite/**/*"]
}
```
**作用**:为主进程和预加载脚本提供类型检查,这两个运行在 Node.js 环境,需要 Node.js 内置类型(如 `process``path``fs`)。
### tsconfig.web.json(浏览器环境)
**对应**`src/renderer/`
```json
{
"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 的 `global` vs 浏览器的 `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`,但需要为三个进程分别配置:
```typescript
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) │
└──────────────────────────────────────────┘
```