# 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 │ │ ◄─────────────────────────────│ ▼ ▼ ``` ```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 ``` - `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.json(Node.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) │ └──────────────────────────────────────────┘ ```