人人都会AI编程

15.1 企业级项目目录架构设计

更新时间:2026-07-11

当 Electron 应用从一个小工具成长为一个需要多人协作、长期维护的产品时,随意堆放的文件结构会成为开发效率的最大杀手。这一节不会给你一个“万能模板”,而是给出一个经过多个真实项目验证的目录设计思路,并解释每一个文件夹存在的理由。你可以根据自己的项目规模对其进行裁剪或扩展。

15.1.1 总体结构概览

一个典型的企业级 Electron 项目目录大致如下。它假设你使用 TypeScript 编写主进程和预加载脚本,使用 React/Vue 等框架编写渲染进程,并通过 electron-builder 完成打包。

my-app/
├── .github/                    # CI/CD 工作流
├── build/                      # 打包相关配置(图标、安装脚本)
├── resources/                  # 额外资源文件(程序图标源文件等)
├── src/
│   ├── main/                   # 主进程代码
│   ├── preload/                # 预加载脚本
│   ├── renderer/               # 渲染进程(前端项目)
│   ├── shared/                 # 主进程与渲染进程共享代码
│   └── common/                 # 通用工具(跨环境可用)
├── tests/                      # 端到端测试、集成测试
├── docs/                       # 内部文档
├── scripts/                    # 开发辅助脚本
├── package.json
├── tsconfig.json               # 主进程 TypeScript 配置
├── tsconfig.node.json          # Node 环境配置(供 vite 等使用)
├── electron-builder.yml        # 打包配置
└── .gitignore

这个结构并不是一开始就要全部创建好,而是在项目演进过程中逐步形成的。但提前了解全貌,能让你避免后期的重构痛苦。

15.1.2 src/main/ —— 主进程的家

主进程是应用的“后台大脑”,负责窗口管理、系统交互及进程通信中枢。建议将主进程代码按功能模块拆分,而不是把所有逻辑扔进一个 main.ts

src/main/
├── index.ts                # 入口:初始化 app,创建主窗口
├── window/                 # 窗口管理
│   ├── mainWindow.ts       # 主窗口创建与配置
│   ├── splashWindow.ts     # 启动闪屏
│   └── windowManager.ts    # 窗口生命周期统一管理
├── ipc/                    # IPC 处理模块
│   ├── fileHandlers.ts     # 文件读写、对话框等
│   ├── systemHandlers.ts   # 系统信息、快捷键、托盘等
│   └── index.ts            # 统一注册所有 IPC 处理器
├── services/               # 业务逻辑层
│   ├── updater.ts          # 自动更新
│   ├── logger.ts           # 日志记录(electron-log)
│   └── database.ts         # 数据库连接(如 better-sqlite3)
├── utils/                  # 主进程专用工具函数
└── constants.ts            # 常量(如 IPC 通道名称)

为什么这样分:

  • IPC 处理器独立成模块:当应用的功能增多,IPC 通道可能达到几十个。把它们按领域(文件、系统、用户)分别写在不同的文件里,并通过 index.ts 统一注册,可以避免 ipcMain.handle 散落各处难以维护。
  • 业务逻辑抽离到 services:让 IPC 处理器只负责参数校验和结果返回,真正的逻辑由 service 层完成。这样便于单元测试和逻辑复用。

15.1.3 src/preload/ —— 安全桥梁

预加载脚本是连接主进程和渲染进程的唯一安全通道。企业级项目通常会对预加载脚本进行分类,为不同的窗口暴露不同的 API。

src/preload/
├── main.preload.ts         # 主窗口使用的预加载脚本
├── setting.preload.ts      # 设置窗口(如果需要)
└── types.ts                # 定义暴露给渲染进程的 API 类型

main.preload.ts 中,你会使用 contextBridge 暴露一个有限的对象,比如:

import { contextBridge, ipcRenderer } from 'electron';

contextBridge.exposeInMainWorld('electronAPI', {
  openFileDialog: () => ipcRenderer.invoke('dialog:openFile'),
  saveFile: (content: string) => ipcRenderer.invoke('file:save', content),
  // 绝不要暴露 ipcRenderer.send 或 on 的原始方法,
  // 而是只提供封装好的语义化函数
});

关键原则: 渲染进程不知道 ipcRenderer 的存在,只知道 window.electronAPI 上那几个确切的方法。这既安全,又让前端开发者调用时有完整的类型提示(配合 types.ts 中的声明)。

15.1.4 src/renderer/ —— 前端的世界

渲染进程本质上就是一个标准的现代前端项目。你可以使用 Vite 创建 Vue 或 React 应用,然后把它放在 src/renderer/ 下。Electron 在开发时加载 Vite 的开发服务器地址,生产时加载打包后的静态文件。

src/renderer/
├── public/                 # 静态资源
├── src/
│   ├── assets/             # 图片、字体等
│   ├── components/         # 通用 UI 组件
│   ├── views/              # 页面视图
│   ├── hooks/              # 自定义 hooks(React)或 composables(Vue)
│   ├── stores/             # 状态管理
│   ├── utils/              # 工具函数
│   ├── App.tsx             # 根组件
│   └── main.ts             # 入口文件
├── index.html
├── vite.config.ts
└── package.json            # 前端依赖单独管理(可选)

要不要给渲染进程单独一个 package.json
对于大中型项目,将前端依赖与 Electron 依赖分开是推荐的做法。这样做的好处是:

  • 渲染进程的构建工具(Vite/Webpack)配置更简洁。
  • package.json 中不会混杂 electronvue/react 的依赖,语义清晰。
  • monorepo 工具(如 pnpm workspace)能轻松管理这种结构。

如果项目较小,把所有依赖放在根目录的 package.json 中也完全可以。

15.1.5 src/shared/ —— 代码共享层

主进程和渲染进程经常需要共享一些常量、类型定义甚至工具函数。将它们放在 shared/ 目录下,避免循环引用或环境冲突。

src/shared/
├── ipcChannels.ts          # IPC 通道名称常量(两边都会引用)
├── types/                  # 共享的类型定义
│   ├── file.ts
│   └── user.ts
└── utils/                  # 与运行环境无关的工具函数
    └── format.ts

例如,ipcChannels.ts 可以统一定义所有通道名,避免拼写错误:

export const IPC_CHANNELS = {
  FILE_OPEN: 'file:open',
  FILE_SAVE: 'file:save',
  APP_GET_VERSION: 'app:getVersion',
} as const;

主进程注册 handler 时使用 IPC_CHANNELS.FILE_OPEN,渲染进程调用时也使用同一个对象,修改通道名时只需改动一处。

15.1.6 构建与配置层

  • build/ 目录存放打包相关的资源,比如 Windows 和 macOS 的应用图标(.ico, .icns)、安装程序的脚本(NSIS 配置)、代码签名证书路径等。
  • electron-builder.yml 是企业级项目普遍使用的打包配置文件。它远比在 package.json 中写 "build" 字段更清晰,支持多环境、多平台的条件配置。
  • resources/ 用来放置构建时需要的额外文件,例如程序的图标源文件(.png, .svg),它们会被 electron-builder 根据配置转换成对应平台的格式。

15.1.7 测试目录 tests/

企业级项目不能没有自动化测试。建议至少在 tests/ 下包含:

  • e2e/ — 端到端测试,使用 Playwright 或 Spectron 启动完整的 Electron 应用,模拟用户操作。
  • integration/ — 主进程服务层的集成测试,直接调用 Node.js 模块。
  • unit/ — 共享工具函数、预加载脚本逻辑的单元测试。

测试代码与源码分离,能保持项目目录清爽,也让 CI 流程更容易识别测试文件。

15.1.8 划分背后的真实考量

你可能会觉得这套结构有点“过度设计”,但真实情况是:一个中型以上的 Electron 应用,如果没有清晰的目录约定,不出三个月就会变成新人不敢碰的“遗产代码”。这些设计背后有几个朴素的工程原则:

  1. 关注点分离:主进程、预加载、渲染进程各司其职,不允许跨层级直接调用。shared 层打破这一隔离的唯一合法通道。
  2. 面向变化:把可能频繁修改的部分(UI、业务逻辑)和相对稳定的部分(IPC 通道定义、打包配置)分开,减少改动时的风险面。
  3. 易于协作:当你告诉新同事“文件相关的 IPC 处理全在 src/main/ipc/fileHandlers.ts”时,他就能立刻定位问题,而不是在一个上千行的 main.js 里翻找。
  4. 利于测试:将业务逻辑从主进程入口中剥离到 services 层,意味着你可以用 Jest 或 Vitest 直接测试它们,而无需启动整个 Electron 应用。

最终,目录架构不是教科书,而是一张根据项目实际需要不断打磨的地图。初学者可以从一个简单的单文件 main.js 开始,随着功能的增多,逐步向上面这种结构演进。关键是要在复杂度增长到失控之前,建立起这套秩序。