本节摘要:搭建 Electron 工程的第一步是认清"一个应用、三个构建目标"(主进程、preload、前端页面),并借助 Electron Forge 等脚手架获得规范的初始结构。本节覆盖 Node 环境与包管理器选择、Forge 初始化产物的逐目录解读、开发期热重载的三路方案,以及常见的环境坑(网络镜像、版本锁定、跨平台差异)。
开发机上只需要两样东西:一份 Node.js 运行时(建议 LTS 版本)和一个包管理器(npm 足够,pnpm 更省磁盘)。装好后,官方推荐的起手式是脚手架:
$ npm create electron-app@latest my-app √ Created .gitignore √ Created package.json √ Created forge config √ Created vite config √ Linted & formatted
跑起来应用窗口就弹出来了——但在按下回车之前,值得先想清楚这个命令到底生成了什么。Electron 项目与纯前端项目最大的差异,是它天然有三个运行环境不同的构建目标:
主进程目标:跑 Node 环境,模块体系可直接用 CommonJS 或编译后的 ESM,构建关注点是把 Node 依赖打包或外置。
preload 目标:同样面向 Electron,但产物要求极为苛刻——沙箱下只能用极有限的 API,且体积越小越好。
前端目标:标准 Web 应用,Vite 或 Webpack 照常处理,产物是 HTML/CSS/JS 静态资源。
三个目标的产物最终要被组装到一起:主进程加载打包后的前端页面。脚手架的价值就是替你把这套多目标结构配好。逐目录看一遍初始化产物:
my-app/ ├─ forge.config.js 打包与发布配置(第六章主角) ├─ vite.main.config.mjs 主进程构建配置 ├─ vite.preload.config.mjs preload 构建配置 ├─ vite.renderer.config.mjs 前端构建配置 └─ src/ ├─ main.js 主进程入口:创建窗口、注册 IPC ├─ preload.js 预加载脚本:暴露白名单桥 └─ renderer/ 前端应用:页面、组件、样式
三份 Vite 配置并列在根目录,正是"三个构建目标"的物理呈现。看懂这个结构,就理解了为什么后面所有工程操作都要先问一句:这行代码属于哪个目标?
脚手架生成的主进程入口已经体现了第二章的全部安全默认值,值得逐行读懂:
// 主进程入口(脚手架生成的典型形态,注释为解读) const { app, BrowserWindow } = require('electron'); const path = require('node:path'); const createWindow = () => { const win = new BrowserWindow({ width: 1024, height: 720, webPreferences: { preload: path.join(__dirname, 'preload.js'), sandbox: true, // 第二章讲过的默认,显式写出便于审计 contextIsolation: true } }); // 开发环境连前端开发服务器,生产环境加载打包产物 if (process.env.NODE_ENV === 'development') { win.loadURL(process.env.VITE_DEV_SERVER_URL); win.webContents.openDevTools({ mode: 'bottom' }); } else { win.loadFile(path.join(__dirname, '../renderer/index.html')); } }; app.whenReady().then(() => { createWindow(); app.on('activate', () => { // macOS 特有:Dock 图标点击时重建窗口 if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); }); app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); // macOS 惯例:窗口关完不退出 });
四个平台约定浓缩在这二十行里:开发/生产双加载路径、macOS 的 activate 重建、非 mac 平台关窗即退。这些"习俗"不是 API 知识而是平台礼仪,漏写的症状都很有迷惑性——比如 mac 上窗口关完应用还挂在 Dock 里,用户点图标却没反应。
开发体验的大头是热重载,但三个目标的刷新手段完全不同,一张账算清楚:
前端改动:Vite 开发服务器的 HMR 兜底,页面局部热替换,体验与普通 Web 开发无异——前提是窗口加载的是开发服务器地址。
preload 改动:没有 HMR,只能整窗重载;主流插件会监听 preload 产物变化后调用 webContents.reload。
主进程改动:最重,必须重启整个应用(主进程就是应用本身);插件方案是检测到主进程产物变化后杀掉旧进程、拉起新进程,并保留窗口状态。
$ npm start 开发模式:三路监听全开 [main] built in 320ms [preload] built in 45ms [renderer] Local: http://localhost:5173/ 应用窗口已启动,改 renderer 组件 → 页面局部刷新 改 preload → 窗口整页 reload;改 main → 应用自动重启
理解三路差异还有一个实用收益:报错时能立刻定位该去哪个目标的控制台找日志。前端报错在 DevTools Console,主进程报错在启动它的终端,混着看经常空欢喜一场。
几类高频坑提前打预防针。其一,下载镜像:首次安装要拉 Electron 二进制(上百兆),网络不通时配置镜像变量是标准操作,忘了配的典型症状是 install 卡在最后一步超时。其二,版本锁定的双刃剑:Electron 版本决定 Chromium 与 Node 版本(2.2 节),锁定主版本是纪律,但锁太死会错过安全补丁——建议锁定 major、跟进 minor。其三,Windows 的路径与权限:构建脚本里的路径分隔符、杀毒软件对产物的误报,都是 Windows 上高出镜率的杂项。其四,Node 版本与引擎声明:在 package.json 里声明 engines 字段,能让同事的错误环境在 install 阶段就暴露,而不是跑到某个神秘运行时错误。
骨架立住了,下一节把流水线的主体设备装上:构建工具链与 TypeScript 如何跨三个目标统一工作。