对应:https://www.electronjs.org/zh/docs/latest/tutorial/tutorial-first-app
一、本节在教程里的位置与目标
- 位置:Electron 教程第 2 部分(前面还有「基本要求」等)。
- 目标 :会建一个最小的 Electron 项目,能在终端里真正跑起来一个带窗口的应用。
- 后续预告:预加载脚本、加功能、打包、发布与更新会在后面章节讲。
二、环境与工具链要点
|------------------------------|---------------------------------------------------------------------------------|
| 要点 | 说明 |
| Windows 不要用 WSL 跟本教程 | 在 WSL 里跑容易踩坑,尽量用原生 Windows 环境或按官方文档单独处理。 |
| 以 npm 为中心 | 项目以 package.json 为入口配置;Electron 打包链依赖磁盘上真实存在的 node_modules。 |
| Yarn Berry / pnpm | 若用它们,需按文档配置(如 Yarn 的 nodeLinker: node-modules、pnpm 的 hoisted 等),否则和打包工具预期不一致。 |
三、创建项目的步骤⭐
- 建目录 →
mkdir my-electron-app && cd my-electron-app - npm init → 生成
package.json - 约定 :
- main********指向 main.js(主进程入口)
author、license、description先随便填也行,但后面打包会用到,建议认真填
- 安装 Electron :
npm install electron --save-dev - 为什么是 devDependency?
- 运行时你会用到 Electron 的 JS API,但真正绑定的原生二进制 由打包阶段处理;日常开发用 dev 依赖即可,不必当成「线上 npm 生产依赖」那套来理解。
- 不要 --ignore-scripts:安装时要能跑 Electron 的 postinstall,否则可能装不完整。
- 加 .gitignore:至少忽略
node_modules(可直接用 GitHub 的 Node 模板)。
四、主进程:从一行 log 到打开窗口
4.1 入口与启动命令
- package.json********里的 main:指定主进程入口文件(例如
main.js)。 - scripts.start:
"start": "electron ."electron .= 在当前目录找main指向的文件,以开发方式跑起来。
- 主进程本质是 Node 环境 ,所以一开始哪怕只有
console.log也能验证入口是否配对。
4.2 最小窗口代码在做什么(概念版)⭐
require('electron')里常用:app(应用生命周期)、BrowserWindow(窗口)。- createWindow():
new BrowserWindow({ width, height }),再 win.loadFile('index.html') 加载本地页面。 - app.whenReady().then(...):在 ready********之后 才能安全创建窗口。
- 教程强调:优先用 app.whenReady(),而不是自己
app.on('ready'),可避免一些历史坑(官方 issue 有说明)。
- 教程强调:优先用 app.whenReady(),而不是自己
4.2.1 app:应用程序的生命周期⭐️
总结:
- app:
- 应用模块/应用程序对象,控制应用生命周期;是整个electron程序的"总开关+管家"
- 用途:app.whenReady()(准备好了再干活)、app.quit()(退出)、监听 window-all-closed、activate 等
- BrowserWindow:
-
浏览器窗口,屏幕上的一个活多个窗口
-
用途:设置宽高、loadFile / loadURL、关窗、最小化等和"某一个窗口"相关的事。
const { app, BrowserWindow } = require('electron')
/*
app:应用程序的生命周期
BrowserWindow:浏览器窗口
createWindow:创建浏览器窗口
loadFile:加载HTML文件
whenReady:当应用程序准备好时创建窗口
activate:当应用程序激活时创建窗口
window-all-closed:当所有窗口关闭时退出应用程序
*/
const path = require('path')function createWindow() {
const win = new BrowserWindow({
width: 900,
height: 640,
webPreferences: {
// 学习阶段先保持默认;后续接 Vue3 时再按需加 preload、关闭 nodeIntegration 等
},
})win.loadFile(path.join(__dirname, 'index.html'))
}app.whenReady().then(() => {
createWindow()app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
-
4.3 渲染进程(和你前端工作的关系)
- 窗口里显示的每个页面,跑在单独的渲染器进程里。
- 渲染进程里可以用熟悉的 Web API、打包工具(webpack 等)、Vue/React ------和做网页很像,但和主进程是隔离的(安全模型后面「预加载脚本」会展开)。
4.4 index.html 里 CSP 的意义(先有个印象)
Content-Security-Policy限制默认只信任同源、script-src 'self',是安全基线;后面接 Vue、接外部脚本时要和 CSP 一起设计,否则容易被策略拦住。
五、窗口生命周期:为什么要多写几行
这是「桌面端体验」和「纯网页」差别很大的地方。
- Windows / Linux :习惯上关掉所有窗口 = 退出应用
- 监听 window-all-closed,若非 macOS(
process.platform !== 'darwin')则 app.quit()。
- 监听 window-all-closed,若非 macOS(
- macOS :关掉窗口后,Dock 里应用常还在 ;用户再点图标时期望再开一个窗口
- 在 ****app.whenReady()****之后 监听 activate,若
BrowserWindow.getAllWindows().length === 0就再调一次 createWindow()。
- 在 ****app.whenReady()****之后 监听 activate,若
- 平台判断 :用 process.platform,Electron 主要关心 win32****/**** linux****/**** darwin。
六、可选:用 VS Code 同时调试主进程 + 渲染进程(结构理解)
- Main :
type: node,runtimeExecutable指向项目里的electron,并加 --remote-debugging-port=9222。 - Renderer :
type: chrome,request: attach,挂到该端口。 - Compound :
Main + renderer一次启两套调试。 - 注意 :附加调试器有「连上慢」的问题,前几行可能断不到,可用刷新或短暂
setTimeout缓解。
七、和 Vue3 结合的「预习心态」(本节教程没细讲,但你要知道)
- Vue3 的 UI :通常跑在渲染进程 (例如
index.html挂一个#app,或用 Vite 开发服务器------那是进阶整合方式)。 - 主进程 :负责窗口、系统菜单、文件读写等「有权限」的事;不要 在渲染进程里随便
require('fs')乱来,后面会学 preload + contextBridge 做安全桥接。
本节你只要先建立心智模型:一个 Node 主进程 + 多个 Chromium 渲染进程。
八、一句话总结(背这个就够复盘)
Electron 应用是 npm 项目 ;main********指定主进程 JS ,主进程用 app****+**** BrowserWindow 在 whenReady********后 创建窗口并 loadFile;页面在渲染进程 里像网页一样跑;关窗是否退出 要按 Windows/Linux 与 macOS 分别处理 window-all-closed****/**** activate;Electron 装在 devDependencies,安装时不要禁掉 postinstall 脚本。
如果你愿意,下一篇学习记录我可以按同样结构帮你整理「使用预加载脚本」那一章,并顺带画一张「主进程 / 预加载 / 渲染进程 / Vue」数据流简图,方便你对接 Vue3。
九、问题总结
|----------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 问题 | 原因 | 解决办法 |
| 安装/启动报 Electron failed to install correctly******,下载很慢或****** Fetch******/****** terminate报错 | Electron 本体(Chromium)要从外网拉 zip,国内常慢或中断;v42 还在首次运行 electron时才下载 | 项目里用 .npmrc配 electron_mirror(如 npmmirror);失败则删 node_modules/electron 再 npm install,再 npm run start |
| Node 版本不对、装依赖报错 | Electron 42 要求 Node ≥ 22.12 | 用 nvm use 22(或同等级 22.x),与 package.json 里 engines 一致 |
| app为 undefined******/****** whenReady报错 | 环境里有 ELECTRON_RUN_AS_NODE=1 时,require('electron') 会变成路径字符串而不是 API | start脚本里先 env -u ELECTRON_RUN_AS_NODE再执行 electron .(你当前 macOS 方案) |
为什么脚本start要由"electron ."改成"start": "env -u ELECTRON_RUN_AS_NODE electron ."?
原因说明
ELECTRON_RUN_AS_NODE=1 时,Electron 会按 「当成普通 Node 来跑」 的模式工作(给工具链、脚本用的能力),不会按桌面应用主进程那样注入真正的 electron API。
这时在入口里写 require('electron'),很容易拿到的是 npm 包那层包装(甚至是路径字符串),没有 app、BrowserWindow 等,于是会出现 app 为 undefined、whenReady 报错之类的问题。
某些终端/IDE/Agent 环境会 默认带上 ELECTRON_RUN_AS_NODE=1,所以用:
env -u ELECTRON_RUN_AS_NODE electron .
表示:只对这一次启动 从环境里 去掉 这个变量,让进程走 正常的 Electron 桌面应用模式。
- 若你本机终端里 从来没有 设置过
ELECTRON_RUN_AS_NODE,env -u一般也没副作用(未设置时去掉它仍然合法)。 - 若你 100% 确定 环境干净,也可以继续用
electron .;加上env -u是为了 在「被污染」的环境里也能稳定启动。