目录标题
前言:为什么选择Electron?
作为前端开发者,你可能遇到过这些场景:
- 需要将Web应用打包成桌面客户端
- 要求访问本地文件系统或硬件设备
- 希望实现多窗口复杂交互
- 需要离线运行且保持原生应用体验
Electron 完美解决了这些问题!使用HTML/CSS/JavaScript即可构建跨平台桌面应用,VSCode、Slack等知名应用均基于Electron开发。本文将带你从零开始,系统掌握Electron开发全流程。
一、Electron基础篇:环境搭建与核心概念
1.1 开发环境准备
# 创建项目(建议Node.js版本≥16.x)
mkdir my-electron-app && cd my-electron-app
npm init -y
# 安装Electron(国内用户推荐使用镜像)
npm config set ELECTRON_MIRROR https://npmmirror.com/mirrors/electron/
npm install electron --save-dev
# 验证安装
npx electron -v
1.2 项目结构解析(关键文件详解)
# 典型项目结构(注释版)
my-electron-app/
├── package.json # 项目配置文件
├── main.js # 主进程入口(核心逻辑)
├── preload.js # 预加载脚本(安全桥梁)
├── renderer/ # 渲染进程资源
│ ├── index.html # 窗口主页面
│ ├── main.css # 样式文件
│ └── app.js # 渲染进程脚本
└── assets/ # 静态资源目录
│ ├── icons/ # 应用图标
│ └── fonts/ # 字体文件
关键文件说明:
// main.js(主进程核心)
const { app, BrowserWindow } = require('electron')
const path = require('path')
// 创建窗口函数
function createWindow() {
const win = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js') // 指定预加载脚本
}
})
// 加载本地页面(开发环境可改为 loadURL)
win.loadFile('renderer/index.html')
}
// 应用启动回调
app.whenReady().then(createWindow)
<!-- renderer/index.html(渲染进程示例) -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Electron App</title>
<link rel="stylesheet" href="main.css">
</head>
<body>
<h1>Hello Electron!</h1>
<script src="app.js"></script>
</body>
</html>
1.3 核心概念解析(文字版架构说明)
Electron双进程模型:
- 主进程(Main Process)
- 使用Node.js环境
- 通过BrowserWindow创建窗口
- 访问系统API(文件系统、菜单等)
- 典型入口文件:main.js
- 渲染进程(Renderer Process)
- 每个窗口对应一个渲染进程
- 运行在Chromium环境中
- 默认无法直接访问Node.js
- 通过预加载脚本安全通信
进程通信流程示例:
// preload.js(安全通信桥梁)
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
send: (channel, data) => ipcRenderer.send(channel, data),
receive: (channel, func) => {
ipcRenderer.on(channel, (event, ...args) => func(...args))
}
})
// 渲染进程调用
window.electronAPI.receive('update', (data) => {
console.log('收到主进程消息:', data)
})
// 主进程监听
ipcMain.on('request', (event, data) => {
event.sender.send('update', '响应数据')
})
架构要点总结:
- 主进程是应用核心,管理所有窗口
- 渲染进程通过预加载脚本安全通信
- IPC通信需遵循"渲染进程->主进程->渲染进程"的闭环
- 敏感操作必须通过主进程代理执行
二、核心机制详解:主进程与渲染进程
2.1 主进程开发实战
创建浏览器窗口:
// main.js
const { app, BrowserWindow } = require('electron')
const path = require('path')
let mainWindow
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
// 加载本地文件
mainWindow.loadFile('renderer/index.html')
// 打开开发者工具
mainWindow.webContents.openDevTools()
}
app.whenReady().then(createWindow)
2.2 渲染进程安全实践
预加载脚本示例:
// preload.js
const { contextBridge, ipcRenderer } = require('electron')
// 安全暴露API到渲染进程
contextBridge.exposeInMainWorld('electronAPI', {
openFile: () => ipcRenderer.invoke('dialog:openFile')
})
渲染进程调用:
// renderer/app.js
document.getElementById('file-btn').addEventListener('click', async () => {
const filePath = await window.electronAPI.openFile()
console.log('选择的文件:', filePath)
})
2.3 进程通信全解析
通信方式 | 适用场景 | 代码示例 |
---|---|---|
ipcMain/ipcRenderer | 点对点通信 | ipcRenderer.send(‘event’) |
remote模块 | 直接调用主进程方法(已废弃) | 不推荐使用 |
contextBridge | 安全暴露API | 见2.2节示例 |
webContents.send | 主进程主动推送 | win.webContents.send(‘update’) |
三、企业级开发实战
3.1 项目脚手架搭建
# 使用官方脚手架
npx @electron-forge/cli import
# 选择模板(这里选webpack)
? Which template would you like to use? webpack
# 启动开发模式
npm start
3.2 典型功能实现
- 系统通知:
// 在主进程中
const { Notification } = require('electron')
function showNotification(title, body) {
new Notification({ title, body }).show()
}
- 菜单定制:
const { Menu } = require('electron')
const template = [
{
label: '文件',
submenu: [
{
label: '打开',
accelerator: 'CmdOrCtrl+O',
click: () => { /* 打开文件逻辑 */ }
}
]
}
]
Menu.setApplicationMenu(Menu.buildFromTemplate(template))
- 本地文件操作:
// 通过IPC实现
// 主进程
ipcMain.handle('readFile', async (event, path) => {
return await fs.promises.readFile(path, 'utf-8')
})
// 渲染进程
const content = await window.electronAPI.readFile('/path/to/file.txt')
3.3调试技巧
主进程调试配置(.vscode/launch.json):
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Main Process",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
"args": [".", "--remote-debugging-port=9222"]
}
]
}
四、进阶开发指南
4.1 性能优化方案
优化方向 | 具体措施 | 效果预估 |
---|---|---|
启动速度 | 使用V8快照 | 提升30%+ |
内存占用 | 禁用不必要的nodeIntegration | 降低20%内存 |
渲染性能 | 启用GPU加速 | 动画更流畅 |
打包体积 | 配置files白名单 | 减少50%体积 |
启用硬件加速:
new BrowserWindow({
webPreferences: {
webgl: true,
experimentalFeatures: true
}
})
4.2 安全加固方案
// 安全配置示例
new BrowserWindow({
webPreferences: {
nodeIntegration: false, // 禁用Node集成
contextIsolation: true, // 开启上下文隔离
sandbox: true, // 启用沙箱
webSecurity: true, // 启用Web安全策略
allowRunningInsecureContent: false // 禁止不安全内容
}
})
五、打包与分发
5.1 使用electron-builder
npm install electron-builder --save-dev
# 配置package.json
{
"build": {
"appId": "com.example.myapp",
"productName": "我的应用",
"directories": {
"output": "release"
},
"files": ["dist/**/*"]
},
"scripts": {
"pack": "electron-builder --dir",
"dist": "electron-builder"
}
}
# 生成安装包
npm run dist
5.2 多平台构建
{
"build": {
"win": {
"target": ["nsis", "portable"]
},
"mac": {
"target": ["dmg", "zip"]
},
"linux": {
"target": ["AppImage", "deb"]
}
}
}
六、常见问题排雷
6.1 典型报错处理
问题:require未定义
✅ 解决方案:
- 检查是否开启contextIsolation
- 确认通过preload脚本暴露必要API
问题:白屏无内容
✅ 排查步骤:
- 检查loadURL/file路径是否正确
- 查看控制台日志(主进程启动时添加-enable-logging参数)
6.2 资源加载优化
// 使用协议拦截
protocol.registerFileProtocol('app', (request, callback) => {
const url = request.url.substr(6)
callback({ path: path.normalize(`${__dirname}/${url}`) })
})
// 在HTML中使用
<img src="app://assets/logo.png">
七、项目实战:开发一个Markdown编辑器
7.1 项目初始化与工程化配置
# 创建项目(使用Electron Forge脚手架)
npx create-electron-app markdown-editor --template=webpack
cd markdown-editor
# 安装依赖库
npm install marked highlight.js showdown fs-extra
# 目录结构升级
src/
├── main # 主进程代码
│ ├── index.js
│ └── fileManager.js
├── renderer # 渲染进程代码
│ ├── assets
│ ├── css
│ ├── js
│ └── index.html
└── preload # 预加载脚本
7.2 核心功能模块设计
// 功能架构图(代码实现版)
const featureMap = {
editor: { // 编辑器模块
realtimePreview: true,
syntaxHighlight: true
},
file: { // 文件管理
autoSave: true,
historyVersion: 5
},
export: { // 导出功能
pdf: true,
html: true
},
system: { // 系统集成
tray: true,
shortcut: true
}
}
7.3 核心功能实现
- 双栏实时预览(核心代码)
// 渲染进程 - 编辑器组件
class MarkdownEditor {
constructor() {
this.editor = document.getElementById('editor')
this.preview = document.getElementById('preview')
// 实时监听输入
this.editor.addEventListener('input', this.debounce(this.updatePreview, 300))
}
// 防抖函数
debounce(fn, delay) {
let timer
return (...args) => {
clearTimeout(timer)
timer = setTimeout(() => fn.apply(this, args), delay)
}
}
// 更新预览
async updatePreview() {
const content = this.editor.value
const html = await window.electronAPI.parseMarkdown(content)
this.preview.innerHTML = html
hljs.highlightAll() // 代码高亮
}
}
- 文件管理系统(主进程)
// main/fileManager.js
const { ipcMain, dialog } = require('electron')
const fs = require('fs-extra')
const path = require('path')
class FileManager {
constructor() {
this.currentFile = null
this.initIPC()
}
initIPC() {
// 打开文件
ipcMain.handle('file:open', async () => {
const { filePaths } = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'Markdown', extensions: ['md'] }]
})
if (!filePaths) return null
this.currentFile = filePaths[0]
return fs.readFile(this.currentFile, 'utf8')
})
// 保存文件
ipcMain.handle('file:save', async (_, content) => {
if (!this.currentFile) {
const { filePath } = await dialog.showSaveDialog({
defaultPath: 'untitled.md'
})
if (!filePath) return false
this.currentFile = filePath
}
await fs.writeFile(this.currentFile, content)
return true
})
}
}
- 原生菜单集成
// main/menu.js
const { Menu, shell } = require('electron')
module.exports = function createMenu() {
const template = [
{
label: '文件',
submenu: [
{
label: '打开',
accelerator: 'CmdOrCtrl+O',
click: (_, browserWindow) => {
browserWindow.webContents.send('menu:open-file')
}
},
{ type: 'separator' },
{
label: '导出为PDF',
click: (_, browserWindow) => {
browserWindow.webContents.send('menu:export-pdf')
}
}
]
},
{
label: '开发',
submenu: [
{ role: 'reload' },
{ role: 'toggleDevTools' }
]
}
]
Menu.setApplicationMenu(Menu.buildFromTemplate(template))
}
7.4 工程化增强
- 自动更新配置(main/updater.js)
const { autoUpdater } = require('electron-updater')
module.exports = function checkUpdates() {
autoUpdater.autoDownload = true
autoUpdater.autoInstallOnAppQuit = true
autoUpdater.on('update-available', () => {
mainWindow.webContents.send('update:available')
})
autoUpdater.on('update-downloaded', () => {
mainWindow.webContents.send('update:downloaded')
})
autoUpdater.checkForUpdates()
}
- 打包配置优化(forge.config.js)
module.exports = {
packagerConfig: {
icon: './src/renderer/assets/icon',
extraResource: ['LICENSE']
},
makers: [
{
name: '@electron-forge/maker-squirrel',
config: {
name: 'markdown_editor'
}
},
{
name: '@electron-forge/maker-zip',
platforms: ['darwin']
}
]
}
7.5 项目效果演示
功能演示代码:
// 主窗口启动逻辑
function createWindow() {
mainWindow = new BrowserWindow({
width: 1400,
height: 900,
webPreferences: {
preload: path.join(__dirname, '../preload/index.js')
}
})
// 加载React应用(基于webpack)
mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY)
// 初始化核心模块
new FileManager()
createMenu()
checkUpdates()
}
到这里,这篇文章就和大家说再见啦!我的主页里还藏着很多 篇 前端 实战干货,感兴趣的话可以点击头像看看,说不定能找到你需要的解决方案~
创作这篇内容花了很多的功夫。如果它帮你解决了问题,或者带来了启发,欢迎:
点个赞❤️ 让更多人看到优质内容
关注「前端极客探险家」🚀 每周解锁新技巧
收藏文章⭐️ 方便随时查阅
📢 特别提醒:
转载请注明原文链接,商业合作请私信联系
感谢你的阅读!我们下篇文章再见~ 💕