Electron实战:主进程、IPC通信与打包分发完整教程

发布时间:2026/10/21 19:10:19
Electron实战:主进程、IPC通信与打包分发完整教程 1. 从零开始为什么要用Electron做桌面程序先说结论如果你手里已经有一个Web项目或者你是个前端开发者想快速做一个Windows、macOS都能跑的桌面应用Electron几乎是门槛最低的那条路。它的核心思路特别直白——用Node.js做后端逻辑用Chromium渲染界面最终打包出一个独立的桌面程序。也就是说你会的HTML、CSS、JavaScript到这里直接全都能用上。这个demo项目我前后折腾了两周左右踩了不少坑也摸清了一些规律。这篇文章不是官方文档的翻译是我自己从环境搭建、主进程与渲染进程分工、IPC通信、打包分发一路走下来的完整记录包含所有能直接抄作业的代码和配置。适合谁来读两类人一是想快速给团队做个内部工具、但不想单独学C#或者Qt的前端同学二是已经听说过Electron、但还没真正把第一个demo跑起来的初学者。下面所有内容都以实际可运行为准我用的版本是Node.js 18 LTS和Electron 28建议你也用LTS版本避免奇奇怪怪的兼容性问题。2. 核心概念拆解主进程、渲染进程和预加载脚本2.1 为什么Electron应用必须分进程Electron应用启动后实际上会创建两类进程主进程Main Process和渲染进程Renderer Process。主进程是应用的入口负责创建窗口、调用系统原生能力、管理应用生命周期渲染进程负责页面展示本质上就是一个Chromium浏览器页面。这两者之间有明确的权限边界渲染进程默认是拿不到Node.js API的这是Electron安全模型的核心。我一开始没搞懂这个设计写代码的时候直接在渲染进程里用require(fs)去读文件结果报错。后来才明白Electron这样做是为了防止页面里的恶意脚本直接操作你的操作系统。理解了这个后面做IPC通信的时候思路就清晰多了。预加载脚本Preload Script是沟通两端的桥梁它运行在一个特殊的环境里既能用有限的Node.js API又能通过contextBridge把安全的API暴露给渲染进程。记住一个原则渲染进程永远不直接接触Node.js的核心模块所有敏感操作都在主进程里做。2.2 进程间通信IPC的三种模式IPCInter-Process Communication是Electron开发里最绕不开的话题。我总结下来其实就三种场景第一种渲染进程向主进程发消息主进程接收并处理比如点击按钮后让主进程读取一个文件。用ipcRenderer.send配合ipcMain.on实现。第二种渲染进程向主进程发消息而且需要拿到返回值比如问主进程“当前系统是什么平台”。这时候要用ipcRenderer.invoke配合ipcMain.handle这个模式支持Promise用起来最顺手。第三种主进程主动向渲染进程推送信息比如后台下载任务进度变了主进程用webContents.send发消息渲染进程用ipcRenderer.on监听。写demo的时候我建议你只用第二种invoke/handle模式因为代码可读性好、不容易乱等基础打牢了再研究其他两种也不迟。3. 环境搭建与第一个窗口5分钟跑起来3.1 环境准备工作你需要先装好Node.js。下载LTS版本安装完后在终端里确认版本号能正常输出。然后创建一个项目目录初始化package.json装Electron依赖。国内网络环境建议设置镜像源否则Electron的二进制文件下载会让你等到怀疑人生。mkdir electron-demo cd electron-demo npm init -y npm config set electron_mirror https://npmmirror.com/mirrors/electron/ npm install --save-dev electron28注意Electron和Node.js的版本没有严格的对应关系但Electron内置的Node版本通常落后于最新版写代码时避免使用太新的Node语法特性。装完之后在package.json里加上启动脚本。然后创建三个文件main.js主进程入口preload.js预加载脚本index.html渲染进程页面这是最标准的Electron目录结构任何教程里的demo几乎都是这三件套。3.2 最小可运行代码与常见启动失败主进程的代码是整个应用的骨架。先写一个最简单的版本创建一个窗口加载一个本地HTML文件// main.js const { app, BrowserWindow } require(electron); const path require(path); function createWindow() { const win new BrowserWindow({ width: 1024, height: 768, webPreferences: { preload: path.join(__dirname, preload.js), contextIsolation: true, nodeIntegration: false } }); win.loadFile(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(); });这里有两个参数特别注意contextIsolation: true将渲染进程与Node.js环境隔离安全必须开启nodeIntegration: false禁止渲染进程直接使用Node.js功能我第一次跑demo的时候双击启动脚本没反应终端也不报错。后来发现是app.whenReady()没有正确执行原因是Electron的缓存出了问题执行了npx electron --version之后才恢复正常。如果你也遇到类似问题先检查终端有没有异常输出再考虑是不是缓存问题。4. 预加载脚本与安全通信正确的数据交换姿势4.1 用contextBridge暴露API在Electron 28的时代安全红线就是三件事contextIsolation必须为truenodeIntegration必须为false预加载脚本用contextBridge.exposeInMainWorld暴露API。下面这段预加载脚本是我demo里实际用到的写法// preload.js const { contextBridge, ipcRenderer } require(electron); contextBridge.exposeInMainWorld(demoAPI, { getPlatform: () ipcRenderer.invoke(get-platform), readFile: (filePath) ipcRenderer.invoke(read-file, filePath), writeFile: (filePath, content) ipcRenderer.invoke(write-file, filePath, content) });渲染进程里就可以通过window.demoAPI.getPlatform()直接调用。注意这里暴露的是函数不要直接把整个ipcRenderer对象暴露出去否则渲染进程就拥有了完整的通信能力安全边界又等于没设。4.2 主进程响应与参数校验主进程侧用ipcMain.handle监听对应的频道。这里有一个容易忽略的点主进程在处理渲染进程传来的数据时一定要做参数校验。渲染进程理论上是可以被篡改的如果它给你传了一个超长字符串或者一个畸形路径你的主进程要有能力拒绝而不是直接报错。// main.js 中补充 const { ipcMain } require(electron); const fs require(fs/promises); ipcMain.handle(get-platform, () process.platform); ipcMain.handle(read-file, async (event, filePath) { if (typeof filePath ! string || !filePath.startsWith(app.getPath(userData))) { throw new Error(非法路径); } return await fs.readFile(filePath, utf-8); });这种写法有两点好处一是数据流单向清晰渲染进程发起请求主进程处理并返回结果二是出错时能很方便地在主进程统一打日志排查问题不用到处翻。5. 完整实操一个文件管理器的demo实现5.1 功能设计与HTML页面搭建光跑起来一个空窗口没什么成就感我做的这个demo是一个极简文件管理器功能是显示当前平台的名称列出用户数据目录下的文件列表点击文件后读取内容显示在页面上还能把编辑后的内容写回去。为什么选这个功能因为它正好覆盖了Electron开发的三个核心场景调用系统能力平台信息、读取文件系统、UI与后端交互。做一遍这个比看十遍文档都管用。index.html的页面骨架如下!DOCTYPE html html head meta charsetUTF-8 titleElectron Demo/title style body { font-family: system-ui, sans-serif; margin: 24px; } .card { border: 1px solid #e0e0e0; border-radius: 8px; padding: 16px; margin-bottom: 16px; } pre { background: #f5f5f5; padding: 12px; border-radius: 6px; white-space: pre-wrap; } button { padding: 8px 16px; border: none; background: #4a67ff; color: #fff; border-radius: 6px; cursor: pointer; } button:hover { background: #3a54e0; } /style /head body div classcard h2当前平台/h2 p idplatform加载中.../p /div div classcard h2文件列表/h2 ul idfileList/ul /div div classcard h2文件内容/h2 textarea ideditor rows8 stylewidth:100%; font-family: monospace;/textarea brbr button idsaveBtn保存内容/button /div script src./renderer.js/script /body /html5.2 渲染进程业务逻辑渲染进程的脚本单独放在renderer.js里这是推荐的写法不要把大段script直接内联到HTML里代码一多就乱。// renderer.js const platformEl document.getElementById(platform); const fileListEl document.getElementById(fileList); const editorEl document.getElementById(editor); const saveBtn document.getElementById(saveBtn); let currentFile null; async function init() { const platform await window.demoAPI.getPlatform(); platformEl.textContent platform; const files await window.demoAPI.listFiles(); fileListEl.innerHTML ; files.forEach((file) { const li document.createElement(li); li.textContent ${file.name} (${file.size} bytes); li.style.cursor pointer; li.addEventListener(click, async () { const content await window.demoAPI.readFile(file.path); editorEl.value content; currentFile file.path; }); fileListEl.appendChild(li); }); } saveBtn.addEventListener(click, async () { if (!currentFile) { alert(请先选择一个文件); return; } await window.demoAPI.writeFile(currentFile, editorEl.value); alert(保存成功); }); init();对应的主进程里需要再加一个list-files的处理ipcMain.handle(list-files, async () { const dir app.getPath(userData); const entries await fs.readdir(dir, { withFileTypes: true }); return entries.map((entry) ({ name: entry.name, size: entry.isFile() ? (await fs.stat(path.join(dir, entry.name))).size : 0, path: path.join(dir, entry.name) })); });这一套跑起来一个能交互的桌面应用就成型了。6. 打包分发让demo变成真正的软件6.1 electron-builder的配置与使用开发完demo之后要把它分享给别人用不能指望对方也装Node.js环境必须打包成可执行文件。打包工具有好几个我用了electron-builder原因是配置相对简单支持多平台输出。先安装npm install --save-dev electron-builder然后在package.json里加一段build配置{ scripts: { start: electron ., dist: electron-builder --win --mac }, build: { appId: com.example.demo, productName: ElectronDemo, files: [main.js, preload.js, renderer.js, index.html], win: { target: [nsis] }, mac: { target: [dmg] } } }跑npm run dist之后dist目录下就会生成安装包。Windows下是exe安装文件macOS下是dmg镜像。6.2 打包体积优化思路Electron打包出来的体积默认很大一个空应用都要150MB以上因为里面塞了一整个Chromium。能优化的方案有几种用electron-builder的asar模式把应用代码压缩打包这默认是开启的尽可能减少files列表里的冗余文件如果应用不涉及屏幕录制或摄像头可以考虑关掉一些用不到的Chromium模块但配置比较麻烦不建议新手折腾。体积这件事我的态度是小工具就别太纠结150MB对于现在的硬盘来说不算什么换来的是开发效率极大提升。如果哪天真需要极致瘦身再考虑用Tauri之类的替代方案那是另一个话题了。7. 常见问题排查与避坑记录7.1 主进程与渲染进程的数据传递异常遇到过最典型的问题就是渲染进程调用window.demoAPI时报undefined。多半是两种原因预加载脚本路径写错了BrowserWindow里的preload路径要指向真实的文件contextIsolation配置为false导致contextBridge暴露的API被覆盖或失效排查方法直接在渲染进程的开发者工具控制台里输入window.demoAPI看一下有没有这个对象。没有的话回到主进程检查路径还是不行就把contextIsolation先设为true重启验证。7.2 窗口白屏与加载失败白屏问题通常是页面加载路径不对。loadFile接收的是相对路径最好用path.join(__dirname, index.html)这种绝对拼接方式避免工作目录不同导致的路径错乱。另外如果页面里有外部CDN资源加载失败也会导致看起来白屏尽量把资源本地化。7.3 应用无法退出与进程残留有时候关掉窗口后进程还停留在后台尤其是在Windows上。常见原因是主进程注册了某些全局监听或定时器没有清理。在window-all-closed事件里执行app.quit()是基本操作但如果还有其它窗口引用没释放照样会残留。排查时打开Windows的任务管理器找到对应的进程强制结束然后逐步注释代码找到占用句柄的地方。8. 经验总结demo之外的进阶方向做完这个demoElectron的基本套路就掌握得差不多了。剩下的进阶方向按推荐顺序排一下第一深入理解主进程的能力边界学一学怎么用net模块发请求、怎么用dialog调用系统原生弹窗、怎么用Tray做系统托盘。这些都是桌面应用高频用到的原生能力。第二研究自动更新机制。electron-builder自带了electron-updater配置一个更新服务器应用启动时自动检测新版本。这个对实际分发特别重要否则每次改BUG都要让用户重新下载安装包体验很差。第三如果有跨平台统一存储的需求可以引入SQLite或者用electron-store做本地持久化。electron-store底层就是JSON文件上手极其简单适合做配置存储。我在实际使用中发现Electron最大的价值不是让前端转行做桌面开发而是让团队里已有的Web技术积累能直接复用。做一个内部工具从立项到拿出第一版demo可能只需要一两天时间。很多时候工具的价值不在于技术多高深而在于解决了一个“手动做太烦、懒得做又总出错”的痛点。Electron恰好把这条路铺平了剩下的就看你自己的场景了。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询