一、前言:为什么选 Electron 做桌面应用?

对前端开发者而言,Electron 最大的优势是 **“零成本跨平台”**—— 用 HTML/CSS/JS 就能开发 Windows、macOS、Linux 通用的桌面应用,无需学习 C++、Swift 等原生语言。像 VS Code、Discord、Figma 桌面端都是基于 Electron 开发的,足以证明其稳定性与扩展性。

本文将带大家从 0 开始,30 分钟实现一个支持 “打开 / 保存文件” 的跨平台记事本,全程附环境配置截图、代码注释、运行效果,新手也能轻松上手。

二、环境搭建(5 分钟搞定)

1. 前置依赖准备
  • Node.js:需安装 v14.17 + 版本(推荐 v16 LTS,兼容性更好),下载地址:Node.js 官
  • 代码编辑器: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

  1. 架构示意图 **

(示意图:左侧为主进程,关联 Node.js 和系统 API;右侧为渲染进程,关联 Chromium 和 UI;中间用双向箭头表示 IPC 通信)

架构详细说明:

  1. 主进程

    • 运行在 Node.js 环境中
    • 具有完整的系统 API 访问权限(如文件系统、网络、进程管理等)
    • 负责创建和管理所有渲染进程
    • 典型操作示例:
      • 读写本地文件
      • 执行系统命令
      • 访问数据库
      • 管理应用生命周期
  2. 渲染进程

    • 基于 Chromium 浏览器引擎
    • 专注于 UI 渲染和用户交互
    • 每个窗口对应一个独立的渲染进程
    • 典型操作示例:
      • 渲染 HTML/CSS
      • 执行 JavaScript 前端逻辑
      • 处理用户输入事件
  3. IPC 通信机制

    • 使用 Electron 提供的 IPC 模块(ipcMain/ipcRenderer)
    • 通信流程示例:
      1. 渲染进程发送请求(如"read-file")
      2. 主进程监听并处理请求
      3. 主进程返回结果或错误信息
      4. 渲染进程接收响应并更新 UI
    • 支持同步和异步两种通信模式

⚠️ 关键安全规则

  • 渲染进程运行在沙箱环境中,无法直接调用:
    • require('fs') 等 Node.js 模块
    • 任何系统级 API
    • 原生 GUI 操作
  • 所有特权操作必须通过 IPC 通道交由主进程处理
  • 这种设计既保证了功能完整性,又防止了潜在的安全漏洞

典型应用场景

  1. 文件管理器:渲染进程显示文件列表 → IPC 请求获取文件数据 → 主进程读取文件系统
  2. 系统监控:渲染进程展示 CPU 使用率图表 → IPC 定时获取系统信息 → 主进程查询系统状态
  3. 数据库应用:渲染进程显示数据表格 → 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分钟验证)

  1. 应用启动 通过终端执行启动命令:
npm start

  1. 运行效果 系统将正常启动记事本应用程序界面

  2. 功能验证

  • 文件打开功能:点击"打开文件"按钮,选择任意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 通信配置错误,或预加载脚本路径不正确
  • 解决方案:
    1. 检查main.js中preload路径是否正确(用path.join(__dirname, 'preload.js'))
    1. 检查preload.js是否正确暴露electronAPI
    1. 打开开发者工具(F12),在 Console 中查看是否有报错
3. 打包后应用启动白屏
  • 原因:文件路径配置错误,打包时未包含必要文件
  • 解决方案:在package.json的build字段中添加"files"配置,指定需打包的文件:

"build": {

"files": [

"main.js",

"preload.js",

"index.html",

"renderer.js"

]

}

八、总结与进阶方向

本文通过 “环境搭建→核心概念→实战开发→打包测试” 的流程,带大家实现了一个基础的 Electron 记事本,掌握了:

  1. Electron 双进程模型与 IPC 通信原理
  1. 系统 API(文件对话框、文件读写)的调用方式
  1. Electron 项目的配置与打包方法

若想进一步提升,可尝试以下进阶功能:

  • 添加自动保存:定时将内容保存到本地缓存,防止意外丢失
  • 支持富文本:集成 TinyMCE 等富文本编辑器,实现字体、颜色设置
  • 自动更新:集成electron-updater,实现应用自动检测更新
  • 系统托盘:最小化应用时隐藏到系统托盘,点击托盘图标恢复窗口

后续会陆续更新 Electron 进阶教程,关注我获取更多实战案例!

Logo

赋能鸿蒙PC开发者,共建全场景原生生态,共享一次开发多端部署创新价值。

更多推荐