4、Electron入门:从架构到第一个应用

Electron 这东西,说白了就是用前端技术写桌面应用。我最早接触它的时候,心里还犯嘀咕:一个浏览器壳子能有多靠谱?后来在项目中用它做了个 EDA 工具的辅助面板,才发现这玩意儿真香。今天咱们就把它掰开揉碎了讲清楚。

4.1 Electron 架构:主进程与渲染进程

Electron 的架构其实不复杂。它有两个核心角色:主进程渲染进程。你可以把它们想象成一个公司里的老板和员工。

  • 主进程:一个应用只有一个。它负责管理窗口、系统菜单、文件对话框这些底层操作。说白了,它就是那个发号施令的人。
  • 渲染进程:每个窗口对应一个。它负责显示页面、处理用户交互。每个窗口就像一个小员工,各干各的活。

为什么会这样设计?我刚开始也觉得多此一举。后来在项目中遇到一个坑:如果所有逻辑都跑在渲染进程里,一旦页面卡死,整个应用就崩了。主进程和渲染进程分开,一个窗口挂了,其他窗口还能正常工作。嗯,这就是架构设计的智慧。

核心要点:主进程是 Node.js 环境,渲染进程是浏览器环境。两者不能直接访问对方的 API。

下面这张图是我自己画的,帮你理清它们的关系:

主进程 (Main Process) Node.js 环境 · 管理窗口 · 系统交互 · 文件操作 一个应用只有一个主进程 渲染进程 1 浏览器环境 窗口 A 的页面逻辑 渲染进程 2 浏览器环境 窗口 B 的页面逻辑 IPC 通信

4.2 创建第一个 Electron 应用

光说不练假把式。咱们直接上手创建一个最简单的 Electron 应用。我习惯用 npm 初始化项目,这样依赖管理更清晰。

第一步,创建项目目录并初始化:

mkdir my-electron-app
cd my-electron-app
npm init -y

第二步,安装 Electron:

npm install electron --save-dev

第三步,创建主进程文件 main.js

const { app, BrowserWindow } = require('electron')

function createWindow () {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false
    }
  })

  win.loadFile('index.html')
}

app.whenReady().then(() => {
  createWindow()
})

第四步,创建 index.html

<!DOCTYPE html>
<html>
<head>
  <title>我的第一个 Electron 应用</title>
</head>
<body>
  <h1>Hello, Electron!</h1>
  <p>这是一个桌面应用</p>
</body>
</html>

第五步,修改 package.json,添加启动脚本:

"scripts": {
  "start": "electron ."
}

然后运行 npm start,就能看到你的第一个 Electron 应用了。

小提示nodeIntegration: true 允许渲染进程使用 Node.js API。但在生产环境中,我建议你关闭它,用更安全的 IPC 方式通信。

4.3 进程间通信(IPC)基础

主进程和渲染进程怎么聊天?靠的就是 IPC(Inter-Process Communication)。Electron 提供了 ipcMainipcRenderer 两个模块。

我曾经在项目中犯过一个低级错误:直接在渲染进程里用 require('fs') 读写文件。结果安全策略一开,全崩了。后来老老实实用 IPC,反而更清晰。

来看一个最简单的 IPC 示例:

主进程(main.js)

const { ipcMain } = require('electron')

ipcMain.handle('get-user-data', async (event, userId) => {
  // 模拟从数据库获取数据
  return { id: userId, name: '张三', role: '工程师' }
})

渲染进程(renderer.js)

const { ipcRenderer } = require('electron')

async function loadUserData() {
  const userData = await ipcRenderer.invoke('get-user-data', 123)
  console.log(userData)
  // 输出: { id: 123, name: '张三', role: '工程师' }
}

这个模式叫「请求-响应」。渲染进程发请求,主进程处理并返回结果。简单、安全、好用。

注意:不要在主进程里直接操作 DOM,也不要在渲染进程里调用系统 API。各司其职,才能避免踩坑。

除了 invoke/handle,还有 send/on 模式,适合单向消息推送。比如主进程通知渲染进程「文件下载完成」:

// 主进程
win.webContents.send('download-complete', { filePath: '/tmp/file.pdf' })

// 渲染进程
ipcRenderer.on('download-complete', (event, data) => {
  alert(`文件已下载到:${data.filePath}`)
})

这两种模式基本覆盖了 90% 的通信场景。你想想看,是不是很简单?

4.4 避坑指南

最后分享几个我踩过的坑:

  • contextIsolation 一定要理解清楚:默认是 true,这时候渲染进程不能直接访问 Node.js API。如果你非要开,记得评估安全风险。
  • 开发时记得开 DevToolswin.webContents.openDevTools() 能帮你省掉很多调试时间。
  • 打包前检查依赖:有些 npm 包在 Electron 里可能不兼容,尤其是那些依赖原生模块的。

我曾经在项目里因为忘了设置 webPreferences,导致 IPC 一直不通。查了半天才发现是 contextIsolation 默认开启,而我的代码没做适配。嗯,这种低级错误,犯一次就够了。

好了,Electron 的基础架构和 IPC 就讲到这里。记住:主进程管系统,渲染进程管界面,IPC 是它们之间的桥梁。这个思路捋顺了,后面学什么都会轻松很多。


公众号:蓝海资料掘金营,微信deep3321