electron系列1:Electron不是玩具,为什么桌面应用需要它?

一、一个前端工程师的困惑

2018年,我第一次听到Electron,当时的反应和很多人一样:

"用网页技术写桌面应用?这不就是套个浏览器壳子吗?能好用吗?"

这个偏见在很长时间里影响了我。直到后来,我发现自己每天都在用的VSCode、Slack、Figma、Notion,竟然都是Electron写的。

如果这些产品都在用,那一定不只是"套壳"那么简单。

二、Electron到底是什么?

官方的定义很简洁:

Electron是一个使用JavaScript、HTML和CSS构建跨平台桌面应用程序的框架。

但这句话太抽象了。让我们用一张图来看清楚Electron的架构:

我更喜欢用这个公式来理解它:

Electron = Chromium + Node.js + 原生API

拆开来看:

组成部分 作用 通俗解释 代码示例
Chromium 渲染界面 用你熟悉的HTML/CSS/JS画界面 divbuttonflexbox
Node.js 调用系统能力 读写文件、执行命令、访问数据库 fs.readFile()child_process.exec()
原生API 桌面集成 系统托盘、全局快捷键、通知 TrayglobalShortcutNotification

Electron不是"套壳浏览器",而是让前端代码获得了接近原生应用的系统能力。

三、一个最小Electron应用长什么样?

让我们写一个最简单的Electron应用,来感受它到底做了什么:

项目结构

复制代码
my-electron-app/
├── package.json
├── main.js          # 主进程
├── preload.js       # 预加载脚本(安全桥接)
└── index.html       # 渲染进程(页面)

1. package.json

javascript 复制代码
{
  "name": "my-electron-app",
  "version": "1.0.0",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "devDependencies": {
    "electron": "^28.0.0"
  }
}

2. main.js(主进程)

javascript 复制代码
const { app, BrowserWindow } = require('electron')
const path = require('path')

// 创建窗口的函数
const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),  // 预加载脚本
      contextIsolation: true,  // 开启上下文隔离(安全必须)
      nodeIntegration: false   // 禁止渲染进程直接访问Node(安全必须)
    }
  })

  win.loadFile('index.html')
  
  // 打开开发者工具(开发时使用)
  // win.webContents.openDevTools()
}

// 应用准备就绪后创建窗口
app.whenReady().then(() => {
  createWindow()
  
  // macOS:点击dock图标时如果没有窗口就创建一个
  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

// 所有窗口关闭时退出应用(除了macOS)
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit()
})

3. preload.js(预加载脚本 - 安全桥接)

javascript 复制代码
const { contextBridge, ipcRenderer } = require('electron')

// 安全地暴露API给渲染进程
contextBridge.exposeInMainWorld('electronAPI', {
  // 读取文件(调用主进程的Node能力)
  readFile: (filePath) => ipcRenderer.invoke('read-file', filePath),
  
  // 保存文件
  saveFile: (filePath, content) => ipcRenderer.invoke('save-file', filePath, content),
  
  // 获取系统信息
  getSystemInfo: () => ipcRenderer.invoke('get-system-info')
})

4. index.html(渲染进程 - 这就是普通的Web页面!)

html 复制代码
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>我的第一个Electron应用</title>
  <style>
    body {
      font-family: system-ui, -apple-system, sans-serif;
      margin: 0;
      padding: 20px;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
    }
    .container {
      text-align: center;
      padding: 40px;
    }
    button {
      background: white;
      border: none;
      padding: 12px 24px;
      font-size: 16px;
      border-radius: 8px;
      cursor: pointer;
      margin: 10px;
      transition: transform 0.2s;
    }
    button:hover {
      transform: scale(1.05);
    }
    pre {
      background: rgba(0,0,0,0.3);
      padding: 15px;
      border-radius: 8px;
      text-align: left;
      overflow-x: auto;
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>欢迎使用Electron</h1>
    <p>这是一个真正的桌面应用!</p>
    
    <button id="systemBtn">获取系统信息</button>
    <button id="fileBtn">读取文件</button>
    
    <div id="output">
      <pre>点击按钮,结果会显示在这里...</pre>
    </div>
  </div>

  <script>
    // 调用通过preload暴露的API
    const output = document.getElementById('output')
    
    document.getElementById('systemBtn').onclick = async () => {
      // 注意:这里可以直接调用桌面能力!
      const info = await window.electronAPI.getSystemInfo()
      output.innerHTML = `<pre>${JSON.stringify(info, null, 2)}</pre>`
    }
    
    document.getElementById('fileBtn').onclick = async () => {
      // 这里调用的是Node.js的文件读取能力
      const content = await window.electronAPI.readFile('package.json')
      output.innerHTML = `<pre>${content}</pre>`
    }
  </script>
</body>
</html>

关键理解: index.html里的代码看起来就是普通前端代码,但它能调用window.electronAPI.getSystemInfo()------这是纯Web页面做不到的。

四、那些你不知道的Electron产品

很多人用了很久都不知道这些产品是Electron写的:

产品 类型 日活/用户量 为什么选Electron
Visual Studio Code 代码编辑器 1500万+ 需要Web技术生态 + 性能要求高
Figma 设计工具 400万+ 实时协作能力(WebSocket)+ 跨平台
Notion 笔记应用 1亿+用户 富文本编辑器 + 快速迭代
Slack 企业通讯 1200万+日活 与Web版本复用代码
Discord 游戏语音 1.5亿+用户 低延迟通信 + 游戏内覆盖层
1Password 密码管理 1500万+ 跨平台一致性 + 安全性可控
GitHub Desktop Git客户端 数百万 与GitHub生态无缝集成

这些产品的共同点:

  • 需要快速迭代(Web技术开发效率高)

  • 需要跨平台(Windows/macOS/Linux一套代码)

  • 对界面交互有较高要求(CSS动画、Canvas渲染)

  • 有Web版本,代码复用率高

五、Electron vs 传统方案:选型决策表

这是我给团队做技术选型时用的决策矩阵:

维度 Electron Qt/C++ WPF/C# JavaFX Tauri (Rust)
开发效率 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐ ⭐⭐ ⭐⭐⭐⭐
运行时性能 ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐⭐
内存占用 ~80MB起步 ~20MB ~30MB ~40MB ~10MB
安装包大小 ~70MB ~5MB ~10MB ~40MB ~2MB
跨平台 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
原生体验 ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐
社区生态 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐
学习成本 ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐ ⭐⭐ ⭐⭐⭐

注: Tauri是较新的替代方案,使用系统WebView而非Chromium,体积更小但兼容性略弱。

决策流程图

决策建议:

  • 选Electron:需要快速迭代、跨平台、有Web技术栈积累

  • 选Qt/C++:需要极致性能(游戏引擎、CAD软件)、或嵌入式设备

  • 选WPF:只做Windows、有C#技术栈、需要深度系统集成

  • 选Tauri:追求小体积、团队熟悉Rust、不需要老平台支持

六、Electron的三大核心优势

优势1:Web技术栈的杠杆效应

前端生态的价值:

  • Vue、React、Svelte等框架直接可用

  • npm上有200万+的包可以直接调用

  • 任何Web组件都可以变成桌面功能

代码复用率示意:

一个真实案例:

我们团队曾需要给桌面应用增加Markdown编辑器。如果用Qt实现,需要数周。但Electron下,直接用@toast-ui/vue-editor,两小时就集成完毕。

优势2:真正的跨平台

Electron一套代码可以生成:

平台 输出格式 分发渠道
Windows .exe.msi 官网下载、Microsoft Store
macOS .dmg.app.pkg 官网下载、Mac App Store
Linux .deb.rpm.AppImage APT、Snap Store、Flathub

平台差异处理示例:

javascript 复制代码
// 同一个代码,自动适配不同平台
const { app } = require('electron')

// 文件路径分隔符
const configPath = app.getPath('userData')
// Windows: C:\Users\用户名\AppData\Roaming\应用名
// macOS: /Users/用户名/Library/Application Support/应用名
// Linux: /home/用户名/.config/应用名

// 菜单栏差异
if (process.platform === 'darwin') {
  // macOS:应用菜单在屏幕左上角
  app.dock.show()
} else {
  // Windows/Linux:菜单在窗口内
  win.setMenu(menu)
}

优势3:Web与原生能力的无感融合

这是Electron最强大的地方------你可以在同一个代码文件中写:

javascript 复制代码
// 这是一个完整的Electron功能片段
document.getElementById('save').addEventListener('click', async () => {
  const content = editor.getValue()
  
  // 1. 这是Web代码(DOM操作)
  document.getElementById('status').innerText = '保存中...'
  
  // 2. 这是Node.js代码(写文件)
  await fs.promises.writeFile(currentFilePath, content)
  
  // 3. 这是原生能力(系统通知)
  new Notification('保存成功', { 
    body: `文件已保存到 ${currentFilePath}`,
    icon: 'icon.png'
  })
  
  // 4. 这是Electron原生API(闪烁任务栏图标)
  win.flashFrame(true)
  
  // 5. 更新UI
  document.getElementById('status').innerText = '已保存'
})

前端开发者不需要学习C++、不需要了解Win32 API,就能实现完整的桌面应用。

七、Electron的三大缺点(以及如何应对)

作为资深开发者,我必须诚实地说出它的痛点:

缺点1:内存占用高

问题图示:

应对方案代码:

javascript 复制代码
// 方案1:后台窗口节流
const win = new BrowserWindow({
  webPreferences: {
    backgroundThrottling: true  // 后台窗口降低帧率和定时器频率
  }
})

// 方案2:窗口销毁时彻底释放
win.on('closed', () => {
  win = null  // 解除引用,让GC回收
})

// 方案3:使用内存分析工具
// 在Chrome DevTools中 Memory -> Take heap snapshot
// 找出未释放的对象

缺点2:安装包体积大

体积对比:

应用 安装包大小 安装后大小
原生记事本 <1MB <1MB
Qt计算器 ~5MB ~15MB
最小Electron应用 ~70MB ~180MB
典型Electron应用 100-200MB 300-500MB

应对方案:

javascript 复制代码
// electron-builder配置 - 启用最大压缩
{
  "build": {
    "compression": "maximum",  // 使用7z压缩,比zip小30%
    "asar": true,              // 打包成asar格式
    "files": [
      "!**/*.map",            // 排除source map
      "!**/test/**",          // 排除测试文件
      "!**/docs/**"           // 排除文档
    ]
  }
}

缺点3:启动速度慢

启动流程耗时分析:

优化方案代码:

javascript 复制代码
// 方案1:先创建隐藏窗口
let win = new BrowserWindow({ 
  show: false,  // 先隐藏
  webPreferences: { preload }
})

win.loadURL('app://index.html')

// 准备就绪后再显示
win.once('ready-to-show', () => {
  win.show()
  
  // 如果有需要,再打开DevTools
  // if (isDev) win.webContents.openDevTools()
})

// 方案2:缓存编译结果
// 安装 v8-compile-cache
require('v8-compile-cache')

// 方案3:预创建常用窗口(VSCode模式)
class WindowManager {
  constructor() {
    this.pendingWindows = []
  }
  
  preCreate() {
    // 后台提前创建常用窗口
    const win = new BrowserWindow({ show: false })
    this.pendingWindows.push(win)
  }
  
  getWindow() {
    if (this.pendingWindows.length > 0) {
      const win = this.pendingWindows.pop()
      win.show()
      return win
    }
    return new BrowserWindow()
  }
}

八、什么时候应该(不应该)用Electron?

✅ 适合使用Electron的场景

典型适用案例:

  1. 已有Web版本的产品

    • 直接复用70%+的代码

    • Slack、Discord、Figma都是这么做的

  2. 需要跨平台的工具类应用

    • Markdown编辑器、API测试工具、数据库客户端

    • 例:Postman、TablePlus、Beekeeper Studio

  3. 对界面交互要求高

    • 需要复杂动画、实时协作、Canvas绘图

    • Electron可以使用任何Web动画技术

  4. 快速迭代的创业产品

    • Web前端可以直接转型桌面开发

    • 无需招聘C++/C#团队

❌ 不适合使用Electron的场景

九、Electron vs Tauri:新生代挑战者

近年来Tauri成为热门替代方案,这里做一个详细对比:

对比维度 Electron Tauri
后端语言 Node.js Rust
前端技术 任意Web框架 任意Web框架
WebView 内置Chromium 系统原生WebView
安装包大小 ~70MB起步 ~2MB起步
内存占用 ~80MB起步 ~10MB起步
Windows 7支持 ✅ 完全支持 ⚠️ 需要WebView2
API丰富度 ⭐⭐⭐⭐⭐ ⭐⭐⭐
生态成熟度 ⭐⭐⭐⭐⭐ ⭐⭐⭐
学习成本 低(前端可上手) 中(需要了解Rust)

选型建议:

  • 追求最小体积 + 团队有Rust能力 → 选Tauri

  • 需要最大兼容性 + 团队纯前端 → 选Electron

  • 需要丰富API + 快速开发 → 选Electron

十、Electron的学习路线图

如果决定学习Electron,建议按这个路径:

十一、写在最后:Electron的定位

Electron不是银弹,也不是玩具。

它是一个务实的选择------让Web技术栈的团队能够以可接受的性能和体积代价,交付跨平台桌面应用。

什么时候应该认真考虑Electron?

当你的团队以Web前端为主,当你需要在3-6个月内交付桌面产品,当你愿意用200MB安装包换取80%的代码复用率时,Electron就是最佳答案。

数据支撑:

  • GitHub上Electron有110k+ star

  • 每周npm下载量超过1000万次

  • 全球有超过100万个Electron应用

  • VSCode证明了它可以做到极致性能

  • Figma证明了它可以承载复杂交互

  • Notion证明了它适合现代SaaS产品

相关推荐
ZC跨境爬虫2 小时前
Scrapy工作空间搭建与目录结构解析:从初始化到基础配置全流程
前端·爬虫·python·scrapy·自动化
小村儿2 小时前
连载04-最重要的Skill---一起吃透 Claude Code,告别 AI coding 迷茫
前端·后端·ai编程
_院长大人_3 小时前
Vue + ECharts 实现价格趋势分析图
前端·vue.js·echarts
疯笔码良3 小时前
【Vue】自适应布局
javascript·vue.js·css3
浩星3 小时前
electron系列2:搭建专业Electron开发环境
javascript·typescript·electron
IT_陈寒3 小时前
Vite的alias配置把我整不会了,原来是这个坑
前端·人工智能·后端
万物得其道者成3 小时前
Cursor 提效实战:我的前端 Prompt、审查 SKILL、MCP 接入完整方法
前端·prompt
酒鼎4 小时前
学习笔记(12-02)事件循环 - 实战案例 —⭐
前端·javascript
Bigger4 小时前
第一章:我是如何剖析 Claude Code 整体架构与启动流程的
前端·aigc·claude