Electron 笔记

前言

什么是 Electron

Electron 是一个使用 JavaScript、HTML 和 CSS 构建跨平台桌面应用程序的框架。
它基于 Node.js 和 Chromium,让你可以使用前端技术开发桌面应用。

主进程和渲染进程

  • 主进程:运行 package.json 中 main 脚本的进程,负责创建和管理应用窗口
  • 渲染进程:每个 Electron 窗口运行一个独立的渲染进程,显示网页内容
快速体验

html 页面封装成桌面端应用,可以在 Github 拉取 演示项目 理解原理

项目运行

拉取项目&下载依赖&运行

PowerShell
# Clone this repository
git clone https://github.com/electron/minimal-repro
# Go into the repository
cd minimal-repro
# Install dependencies
npm install
# Run the app
npm start

main.js 代码部分

JavaScript
// 引入控制应用生命周期和创建原生浏览器窗口的模块
const { app, BrowserWindow } = require('electron')
const path = require('node:path')

function createWindow () {
  // 创建浏览器窗口
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    icon:'./build/icon.ico',
    // frame: false,  // 设置为无边框窗口
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')  // 预加载脚本
    }
  })
  
  // 移除菜单栏
  mainWindow.setMenu(null)
  
  // 加载应用的 index.html 文件
  mainWindow.loadFile('index.html')

  // 打开开发者工具(调试用,默认注释掉)
  // mainWindow.webContents.openDevTools()
}

// 当Electron完成初始化并准备创建浏览器窗口时调用此方法
// 某些API只能在此事件发生后使用
app.whenReady().then(() => {
  createWindow()

  app.on('activate', function () {
    // 在macOS上,当点击dock图标且没有其他窗口打开时,
    // 通常会重新创建一个窗口
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

// 当所有窗口都关闭时退出应用,除了macOS
// 在macOS上,应用及其菜单栏通常会保持活动状态,
// 直到用户使用Cmd + Q显式退出
app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') app.quit()
})

// 你可以在这个文件中添加应用特定的主进程代码
// 也可以将它们放在单独的文件中并在这里引入

preload.js 预加载脚本

JavaScript
/**
*预加载脚本在加载`index.html`之前运行 
*在渲染器中。它可以访问web API以及 
*Electron的渲染器处理模块和一些polyfill 
*Node.js函数。 https://www.electronjs.org/docs/latest/tutorial/sandbox
 */
window.addEventListener('DOMContentLoaded', () => {
  const replaceText = (selector, text) => {
    const element = document.getElementById(selector)
    if (element) element.innerText = text
  }

  for (const type of ['chrome', 'node', 'electron']) {
    replaceText(`${type}-version`, process.versions[type])
  }
})

效果展示

%title插图%num

项目打包

使用 electron-builder 打包应用,下载依赖

PowerShell
npm install electron-builder --save-dev
# 或
yarn add electron-builder --dev

配置 package.json(在 package.json 中添加 build 配置,例如)

JSON
{
  "name": "your-app-name",
  "version": "1.0.0",
  "main": "main.js",
  "scripts": {
    "start": "electron .",
    "pack": "electron-builder --dir",  // 生成未打包的应用程序(测试用)
    "dist": "electron-builder"         // 生成安装包
  },
  "build": {
    "appId": "com.example.yourapp",
    "productName": "YourApp",
    "win": {
      "target": "nsis",  // Windows 安装包格式(也可以是 portable、zip 等)
      "icon": "build/icon.ico"  // 应用图标
    },
    "mac": {
      "target": "dmg",  // macOS 安装包格式
      "icon": "build/icon.icns"  // 应用图标
    },
    "linux": {
      "target": "AppImage",  // Linux 安装包格式
      "icon": "build/icon.png"  // 应用图标
    }
  },
  "devDependencies": {
    "electron": "^latest",
    "electron-builder": "^latest"
  }
}

关键配置说明

  • appId:应用唯一标识(如 com.company.appname)。
  • productName:应用名称(显示在安装界面)。
  • win / mac / linux:不同平台的打包配置。
  • target:打包格式(如 Windows 的 nsis、macOS 的 dmg、Linux 的 AppImage)。
  • icon:应用图标路径(需提前准备 .ico.icns 或 .png 文件)

 准备应用图标(你可以使用在线工具(如 icoconvert)生成这些图标,并放在 build/ 目录下)

项目目录
your-project/
├── build/
│   ├── icon.ico    # Windows 图标
│   ├── icon.icns   # macOS 图标
│   └── icon.png    # Linux 图标
├── main.js         # Electron 主进程文件
├── package.json
└── ...

运行打包命令(需要以管理员的方式打开终端才可以打包资源文件)

PowerShell
npm run dist
# 或
yarn dist

打包完成后,生成的安装文件会存放在 dist/ 目录:

  • Windowsyour-app-setup.exe(NSIS 安装包)或 your-app-portable.exe(绿色版)
  • macOSyour-app.dmg(安装镜像)或 your-app.app(可直接运行)
  • Linuxyour-app.AppImage(可执行文件)

可选:代码签名(发布正式版)
如果要发布到应用商店或防止安全警告,需要对应用进行签名

  • Windows:使用 signtool 或购买代码签名证书(如 DigiCert)。
  • macOS:需要 Apple 开发者账号,使用 codesign
  • Linux:通常不需要签名。

    在 package.json 的 build 配置中添加签名信息
PowerShell
"win": {
  "target": "nsis",
  "signingHashAlgorithms": ["sha256"],
  "certificateFile": "path/to/cert.pfx",
  "certificatePassword": "your-password"
},
"mac": {
  "target": "dmg",
  "identity": "Developer ID Application: Your Name (TEAMID)"
}

效果展示

%title插图%num

将 html 页面封装成桌面端

从头开始

从零开始创建一个基础的 electron 的项目。构建项目结构、开发纯净的 electron 项目

前置配置

确认依赖是否安装

PowerShell
node -v  # 检查版本,建议 >=16.x
npm -v   # 检查npm版本

创建项目目录

PowerShell
mkdir my-electron-app
cd my-electron-app

项目初始化 & 安装依赖

PowerShell
npm init -y
npm install electron --save-dev

创建基本文件结构

PowerShell
my-electron-app/
├── package.json
├── main.js    # 主进程文件
├── preload.js # 预加载脚本
└── index.html # 渲染进程页面

配置文件

主进程 (main.js)

main.js
const { app, BrowserWindow } = require('electron')
const path = require('path')

let mainWindow

function createWindow() {
  // 创建浏览器窗口
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js') // 预加载脚本
    }
  })

  // 加载应用页面
  mainWindow.loadFile('index.html')

  // 开发模式下自动打开开发者工具
  if (process.env.NODE_ENV === 'development') {
    mainWindow.webContents.openDevTools()
  }

  // 窗口关闭时触发
  mainWindow.on('closed', () => {
    mainWindow = null
  })
}

// Electron 初始化完成后调用
app.whenReady().then(createWindow)

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

// macOS 点击 dock 图标重新创建窗口
app.on('activate', () => {
  if (mainWindow === null) {
    createWindow()
  }
})

预加载脚本 (preload.js)

preload.js
const { contextBridge } = require('electron')

// 安全地暴露 API 给渲染进程
contextBridge.exposeInMainWorld('electronAPI', {
  doThing: () => console.log('暴露给渲染进程的方法')
})

渲染进程页面 (index.html)

HTML
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Electron App</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
            margin-top: 50px;
        }
    </style>
</head>
<body>
    <h1>Hello Electron!</h1>
    <p>Welcome to your Electron application.</p>
    
    <script>
        // 使用预加载脚本暴露的 API
        window.electronAPI.doThing()
    </script>
</body>
</html>

修改 package.json

JSON
{
  "name": "electron",
  "version": "1.0.0",
  "main": "main.js",
  "scripts": {
    "start": "electron .",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "devDependencies": {
    "electron": "^37.1.0" // 按当前版本替换
  }
}

运行项目

PowerShell
npm start

打包应用 (可选:安装打包工具) 或者可以参考 文打包方案

PowerShell
npm install electron-packager --save-dev

添加打包脚本到 package.json

package.json
"scripts": {
  "package": "electron-packager . --out=dist --platform=win32 --arch=x64 --overwrite"
}

运行打包

PowerShell
npm run package

效果展示

%title插图%num
进程通信

若需要 html 界面和 electron 形成一个通信的效果,例如 html 中按钮退出使其界面关闭。可使用 ipcMain 方案

简单案例

index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <button id="close">取消焦点</button>
    <button id="use">获取焦点</button>
    <button id="btn">退出</button>
    <script>
        document.querySelector('#btn').addEventListener('click', () => {
            window.electronAPI.quitApp()
        })
        document.querySelector('#close').addEventListener('click', () => {
            window.electronAPI.colseFocus()
        })
        document.querySelector('#use').addEventListener('click', () => {
            window.electronAPI.useFocus()
        })
    </script>
</body>

</html>
preload.js
const { contextBridge } = require('electron')
const { ipcRenderer } = require('electron')

// 安全地暴露 API 给渲染进程
contextBridge.exposeInMainWorld('electronAPI', {
  quitApp: () => ipcRenderer.send('quit-app'),
  colseFocus: () => ipcRenderer.send('colse-focus'),
  useFocus: () => ipcRenderer.send('use-focus')
})
main.js
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path')

let mainWindow

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    alwaysOnTop: true,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true, // 推荐开启
      enableRemoteModule: false // 禁用 remote 模块
    }
  })

  mainWindow.setMenu(null)
  mainWindow.loadFile('index.html')

  if (process.env.NODE_ENV === 'development') {
    mainWindow.webContents.openDevTools()
  }

  mainWindow.on('closed', () => {
    mainWindow = null
  })
}

app.whenReady().then(createWindow)

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit()
})

// 更安全的 IPC 通信处理
ipcMain.on('quit-app', () => {
  app.quit()
})

ipcMain.on('colse-focus', (event) => {
  const win = BrowserWindow.fromWebContents(event.sender)
  win.setAlwaysOnTop(false)
  if (!win) return false

  win.blur()
  return true
})

ipcMain.on('use-focus',(event)=>{
  const win = BrowserWindow.fromWebContents(event.sender)
  win.setAlwaysOnTop(true)
  if (!win) return false

  win.focus()
  return true
})

app.on('activate', () => {
  if (mainWindow === null) createWindow()
})

效果展示

%title插图%num