《Electron 基础教程:从进程模型图解到桌面应用开发(含 UI 交互代码 + 运行效果截图)》
对前端开发者而言,Electron 最大的优势是 **“零成本跨平台”**—— 用 HTML/CSS/JS 就能开发 Windows、macOS、Linux 通用的桌面应用,无需学习 C++、Swift 等原生语言。像 VS Code、Discord、Figma 桌面端都是基于 Electron 开发的,足以证明其稳定性与扩展性。本文将带大家从 0 开始,30 分钟实现一个支持 “打开 / 保存文
一、前言:为什么选 Electron 做桌面应用?
对前端开发者而言,Electron 最大的优势是 **“零成本跨平台”**—— 用 HTML/CSS/JS 就能开发 Windows、macOS、Linux 通用的桌面应用,无需学习 C++、Swift 等原生语言。像 VS Code、Discord、Figma 桌面端都是基于 Electron 开发的,足以证明其稳定性与扩展性。
本文将带大家从 0 开始,30 分钟实现一个支持 “打开 / 保存文件” 的跨平台记事本,全程附环境配置截图、代码注释、运行效果,新手也能轻松上手。
二、环境搭建(5 分钟搞定)
1. 前置依赖准备
- 代码编辑器:VS Code(推荐安装 “Electron” 插件,支持语法提示)
- 验证环境:打开终端,输入以下命令,能正常显示版本号即配置成功:
node -v # 显示Node.js版本,如v16.20.2
npm -v # 显示npm版本,如8.19.4
(示意图:终端中显示 Node 和 npm 版本,确认环境正常)
2. 初始化 Electron 项目
步骤 1:创建项目文件夹并进入
# 新建文件夹(名称可自定义)
mkdir electron-notepad
# 进入文件夹
cd electron-notepad
步骤 2:初始化 npm 项目
执行以下命令,会自动生成package.json文件(项目配置核心文件):
npm init -y
步骤 3:安装 Electron 依赖
# 安装Electron到开发依赖(--save-dev表示仅开发环境使用)
npm install electron@28.0.0 --save-dev
⚠️ 注意:若安装卡住,是因为 Electron 需下载 Chromium 内核,可配置淘宝镜像加速:
npm config set electron_mirror https://npm.taobao.org/mirrors/electron/
**
3. 配置 package.json
打开自动生成的package.json,修改 3 个关键配置(红框标注部分):
{
"name": "electron-notepad",
"version": "1.0.0",
"main": "main.js", // 1. 指定主进程入口文件(必须配置)
"scripts": {
// 2. 添加启动脚本,后续用npm start启动应用
"start": "electron .",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"electron": "^28.0.0" // 3. 安装后自动生成,无需修改
}
}
**
三、核心概念:Electron 的 “双进程模型”
在写代码前,必须先理解 Electron 的核心架构 ——主进程 + 渲染进程,否则会踩很多坑!
1. 双进程分工
|
进程类型 |
作用 |
技术栈 |
通信方式 |
|
主进程(Main) |
控制应用生命周期(启动 / 关闭)、访问系统 API(文件 / 对话框) |
Node.js + Electron API |
IPC(Inter-Process Communication) |
|
渲染进程(Renderer) |
展示 UI 界面、处理用户交互(点击 / 输入) |
HTML/CSS/JS + 前端框架 |
IPC |
- 架构示意图 **
(示意图:左侧为主进程,关联 Node.js 和系统 API;右侧为渲染进程,关联 Chromium 和 UI;中间用双向箭头表示 IPC 通信)
架构详细说明:
-
主进程:
- 运行在 Node.js 环境中
- 具有完整的系统 API 访问权限(如文件系统、网络、进程管理等)
- 负责创建和管理所有渲染进程
- 典型操作示例:
- 读写本地文件
- 执行系统命令
- 访问数据库
- 管理应用生命周期
-
渲染进程:
- 基于 Chromium 浏览器引擎
- 专注于 UI 渲染和用户交互
- 每个窗口对应一个独立的渲染进程
- 典型操作示例:
- 渲染 HTML/CSS
- 执行 JavaScript 前端逻辑
- 处理用户输入事件
-
IPC 通信机制:
- 使用 Electron 提供的 IPC 模块(ipcMain/ipcRenderer)
- 通信流程示例:
- 渲染进程发送请求(如"read-file")
- 主进程监听并处理请求
- 主进程返回结果或错误信息
- 渲染进程接收响应并更新 UI
- 支持同步和异步两种通信模式
⚠️ 关键安全规则:
- 渲染进程运行在沙箱环境中,无法直接调用:
- require('fs') 等 Node.js 模块
- 任何系统级 API
- 原生 GUI 操作
- 所有特权操作必须通过 IPC 通道交由主进程处理
- 这种设计既保证了功能完整性,又防止了潜在的安全漏洞
典型应用场景:
- 文件管理器:渲染进程显示文件列表 → IPC 请求获取文件数据 → 主进程读取文件系统
- 系统监控:渲染进程展示 CPU 使用率图表 → IPC 定时获取系统信息 → 主进程查询系统状态
- 数据库应用:渲染进程显示数据表格 → IPC 发送查询请求 → 主进程执行 SQL 语句
四、实战开发:30 分钟实现记事本(附完整代码)
1. 项目结构
先创建以下 4 个文件,最终结构如下(后续逐步填充代码):
electron-notepad/
├─ main.js # 主进程:控制窗口、处理系统API
├─ preload.js # 预加载脚本:安全实现IPC通信(避免漏洞)
├─ index.html # 渲染进程:记事本UI界面
├─ renderer.js # 渲染进程:处理用户交互(按钮点击/输入)
└─ package.json # 项目配置(已创建)
2. 主进程代码(main.js)
负责创建窗口、监听 IPC 请求、调用文件系统 API:
// 引入Electron核心模块
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
// 引入Node.js文件模块(用于读写文件)
const fs = require('fs');
// 引入路径模块(处理文件路径)
const path = require('path');
// 声明主窗口变量(全局变量,避免被垃圾回收)
let mainWindow;
// 1. 创建主窗口函数
function createWindow() {
mainWindow = new BrowserWindow({
width: 800, // 窗口宽度
height: 600, // 窗口高度
title: "Electron记事本", // 窗口标题
// 网页配置(关键:安全通信配置)
webPreferences: {
contextIsolation: true, // 开启上下文隔离(安全要求)
// 预加载脚本路径(__dirname表示当前文件所在目录)
preload: path.join(__dirname, 'preload.js')
}
});
// 加载UI界面(渲染进程)
mainWindow.loadFile('index.html');
// 打开开发者工具(调试用,发布时可删除)
mainWindow.webContents.openDevTools();
}
// 2. 监听渲染进程的"打开文件"请求(通过IPC)
ipcMain.handle('open-file', async () => {
try {
// 打开系统"选择文件"对话框
const { filePaths, canceled } = await dialog.showOpenDialog({
title: "选择要打开的文本文件",
// 只允许选择txt文件(过滤其他格式)
filters: [{ name: '文本文件', extensions: ['txt'] }],
// 只允许选择单个文件
properties: ['openFile', 'singleSelection']
});
// 如果用户取消选择,返回空字符串
if (canceled) return '';
// 读取选中文件的内容(同步读取,简单场景适用)
const fileContent = fs.readFileSync(filePaths[0], 'utf-8');
// 返回文件内容给渲染进程
return fileContent;
} catch (err) {
// 捕获错误(如文件权限不足),返回错误信息
return `打开失败:${err.message}`;
}
});
// 3. 监听渲染进程的"保存文件"请求(通过IPC)
ipcMain.handle('save-file', async (event, content) => {
try {
// 打开系统"保存文件"对话框
const { filePath, canceled } = await dialog.showSaveDialog({
title: "保存文本文件",
defaultPath: "未命名文件.txt", // 默认文件名
filters: [{ name: '文本文件', extensions: ['txt'] }]
});
// 如果用户取消保存,返回失败信息
if (canceled) return { success: false, msg: "已取消保存" };
// 写入内容到文件(同步写入)
fs.writeFileSync(filePath, content, 'utf-8');
// 返回成功信息给渲染进程
return { success: true, msg: `保存成功:${filePath}` };
} catch (err) {
// 捕获错误(如磁盘满了),返回错误信息
return { success: false, msg: `保存失败:${err.message}` };
}
});
// 4. 应用生命周期管理(Electron固定写法)
// 当应用准备就绪后,创建主窗口
app.whenReady().then(createWindow);
// 当所有窗口关闭后,退出应用(Windows/Linux)
app.on('window-all-closed', () => {
// macOS下应用关闭窗口后不会退出,需保留 dock 图标
if (process.platform !== 'darwin') app.quit();
});
// macOS下点击 dock 图标时,重新创建窗口(如果没有窗口)
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
3. 预加载脚本(preload.js)
安全暴露 IPC 接口,避免直接开启nodeIntegration导致漏洞:
// 引入Electron的contextBridge和ipcRenderer(用于安全通信)
const { contextBridge, ipcRenderer } = require('electron');
// 向渲染进程暴露全局API(window.electronAPI)
contextBridge.exposeInMainWorld('electronAPI', {
// 暴露"打开文件"方法(渲染进程调用时,会触发主进程的open-file事件)
openFile: () => ipcRenderer.invoke('open-file'),
// 暴露"保存文件"方法(参数content为渲染进程传递的文件内容)
saveFile: (content) => ipcRenderer.invoke('save-file', content)
});
// ⚠️ 注意:只暴露必要的API,避免暴露敏感能力(如ipcRenderer本身)
4. 渲染进程 UI(index.html)
搭建记事本的界面(包含 “打开文件”“保存文件” 按钮和文本编辑区):
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Electron记事本</title>
<style>
/* 简单样式:让界面更美观 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Microsoft YaHei", sans-serif;
}
/* 工具栏样式 */
.toolbar {
background-color: #f5f5f5;
padding: 10px;
border-bottom: 1px solid #e0e0e0;
}
/* 按钮样式 */
.toolbar button {
padding: 8px 16px;
margin-right: 10px;
border: none;
border-radius: 4px;
background-color: #2f54eb;
color: white;
cursor: pointer;
font-size: 14px;
}
.toolbar button:hover {
background-color: #1d39c4;
}
/* 文本编辑区样式 */
#editor {
width: 100%;
height: calc(100vh - 46px); /* 高度=窗口高度-工具栏高度 */
padding: 15px;
border: none;
outline: none;
font-size: 16px;
resize: none; /* 禁止调整大小 */
}
/* 状态提示样式 */
#status {
position: fixed;
bottom: 10px;
right: 10px;
color: #666;
font-size: 12px;
}
</style>
</head>
<body>
<!-- 工具栏:包含操作按钮 -->
<div class="toolbar">
<button id="openBtn">打开文件</button>
<button id="saveBtn">保存文件</button>
</div>
<!-- 文本编辑区:用户输入内容 -->
<textarea id="editor" placeholder="请输入文本内容..."></textarea>
<!-- 状态提示:显示保存/打开结果 -->
<div id="status"></div>
<!-- 引入渲染进程逻辑脚本 -->
<script src="renderer.js"></script>
</body>
</html>
5. 渲染进程逻辑(renderer.js)
处理用户交互(按钮点击、状态提示),调用预加载脚本暴露的 API:
// 获取页面元素
const openBtn = document.getElementById('openBtn'); // 打开文件按钮
const saveBtn = document.getElementById('saveBtn'); // 保存文件按钮
const editor = document.getElementById('editor'); // 文本编辑区
const status = document.getElementById('status'); // 状态提示区
// 1. 绑定"打开文件"按钮点击事件
openBtn.addEventListener('click', async () => {
// 调用预加载脚本暴露的API,向主进程请求打开文件
const result = await window.electronAPI.openFile();
// 根据结果更新界面
if (result.startsWith('打开失败')) {
// 显示错误提示(红色)
status.style.color = '#f56c6c';
status.textContent = result;
} else if (result) {
// 显示成功提示(绿色),并将文件内容填入编辑区
status.style.color = '#67c23a';
status.textContent = '文件打开成功';
editor.value = result;
} else {
// 用户取消选择,清空提示
status.textContent = '';
}
// 3秒后自动清空提示
setTimeout(() => status.textContent = '', 3000);
});
// 2. 绑定"保存文件"按钮点击事件
saveBtn.addEventListener('click', async () => {
// 获取编辑区的内容(去除前后空格)
const content = editor.value.trim();
// 如果内容为空,提示用户
if (!content) {
status.style.color = '#f56c6c';
status.textContent = '错误:内容不能为空';
setTimeout(() => status.textContent = '', 3000);
return;
}
// 调用预加载脚本暴露的API,向主进程请求保存文件
const result = await window.electronAPI.saveFile(content);
// 根据结果更新状态提示
status.style.color = result.success ? '#67c23a' : '#f56c6c';
status.textContent = result.msg;
// 3秒后自动清空提示
setTimeout(() => status.textContent = '', 3000);
});
五、运行与测试(5分钟验证)
- 应用启动 通过终端执行启动命令:
npm start
-
运行效果 系统将正常启动记事本应用程序界面
-
功能验证
- 文件打开功能:点击"打开文件"按钮,选择任意txt文件,文件内容将自动加载至编辑区域
- 文件保存功能:在编辑区输入文本内容后,点击"保存文件"按钮,选择保存路径即可将内容保存为txt格式文件
- 错误处理机制:当编辑区内容为空时尝试保存,系统将显示红色提示信息"错误:内容不能为空"
六、打包成桌面应用(可选,5 分钟)
开发完成后,可将项目打包成对应系统的安装包(如 Windows 的.exe、macOS 的.dmg),方便分发使用。
1. 安装打包工具
npm install electron-builder --save-dev
2. 配置 package.json
在package.json中添加打包脚本和配置(红框标注新增部分):
{
"name": "electron-notepad",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron .",
"package:win": "electron-builder --win", // 打包Windows版本
"package:mac": "electron-builder --mac" // 打包macOS版本
},
"devDependencies": {
"electron": "^28.0.0",
"electron-builder": "^24.13.3" // 新增:打包工具依赖
},
// 新增:打包配置
"build": {
"appId": "com.example.notepad", // 应用唯一标识(建议域名反转)
"productName": "Electron记事本", // 应用名称
"directories": {
"output": "dist" // 打包输出目录(默认dist)
},
"win": {
"target": "nsis", // 打包为exe安装包
"icon": "build/icon.ico" // 应用图标(可选,需自行准备)
},
"mac": {
"target": "dmg", // 打包为dmg镜像
"icon": "build/icon.icns" // macOS图标(可选)
}
}
}
3. 执行打包
- Windows 用户:执行npm run package:win,打包完成后在dist文件夹中找到Electron记事本 Setup 1.0.0.exe
- macOS 用户:执行npm run package:mac,打包完成后在dist文件夹中找到Electron记事本-1.0.0.dmg
(示意图:dist 文件夹中包含 exe 安装包、未打包的绿色版文件夹、日志文件)
七、常见问题与解决方案
1. 启动应用时报错 “Cannot find module 'main.js'”
- 原因:package.json中main字段配置错误,或main.js文件不存在
- 解决方案:确认main字段值为"main.js",且项目根目录中存在main.js文件
2. 点击 “打开 / 保存文件” 无反应
- 原因:IPC 通信配置错误,或预加载脚本路径不正确
- 解决方案:
-
- 检查main.js中preload路径是否正确(用path.join(__dirname, 'preload.js'))
-
- 检查preload.js是否正确暴露electronAPI
-
- 打开开发者工具(F12),在 Console 中查看是否有报错
3. 打包后应用启动白屏
- 原因:文件路径配置错误,打包时未包含必要文件
- 解决方案:在package.json的build字段中添加"files"配置,指定需打包的文件:
"build": {
"files": [
"main.js",
"preload.js",
"index.html",
"renderer.js"
]
}
八、总结与进阶方向
本文通过 “环境搭建→核心概念→实战开发→打包测试” 的流程,带大家实现了一个基础的 Electron 记事本,掌握了:
- Electron 双进程模型与 IPC 通信原理
- 系统 API(文件对话框、文件读写)的调用方式
- Electron 项目的配置与打包方法
若想进一步提升,可尝试以下进阶功能:
- 添加自动保存:定时将内容保存到本地缓存,防止意外丢失
- 支持富文本:集成 TinyMCE 等富文本编辑器,实现字体、颜色设置
- 自动更新:集成electron-updater,实现应用自动检测更新
- 系统托盘:最小化应用时隐藏到系统托盘,点击托盘图标恢复窗口
后续会陆续更新 Electron 进阶教程,关注我获取更多实战案例!
更多推荐


所有评论(0)