1.概念
-
Electron是一个使用 JavaScript、HTML 和 CSS 构建
桌面应用程序的框架,vscode就是使用它开发的 -
Chromium、Node.js和用于调用系统本地功能的API三大板块
2 多进程
2.1 主进程
- 每个 Electron 应用都有一个单一的主进程,作为应用程序的入口点。
- 主进程在 Node.js 环境中运行,这意味着它具有
require模块和使用所有 Node.js API 的能力
- 窗口管理
主进程的主要目的是使用BrowserWindow模块创建和管理应用程序窗口 - 控制应用程序的生命周期
- 控制原生桌面功能
demo
# 主线程
const { app, BrowserWindow } = require('electron')
const path = require('path')
const createWindow = () => {
// 创建浏览器窗口
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
// 载入html文件
mainWindow.loadFile('index.html')
// Open the DevTools.
mainWindow.webContents.openDevTools()
}
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
2.2 渲染进程
Renderer Process 可以有多个,互相独立不干扰。
每个 Electron 应用都会为每个打开的 BrowserWindow 生成一个单独的渲染器进程。
主要负责: 利用 HTML 和 CSS 渲染页面;利用 JavaScript 实现页面交互效果。
2.3 预加载(preload)脚本
preload脚本运行于渲染器的环境中,能访问 Node.js API 而拥有了更多的权限
const { BrowserWindow } = require('electron')
const win = new BrowserWindow({
webPreferences: {
preload: 'path/to/preload.js'
}
})
3.进程通讯
- 由于主进程和渲染器进程在 Electron 的进程模型具有不同的职责,因此 IPC 是执行许多常见任务的唯一方法,例如从 UI 调用原生 API 或从原生菜单触发 Web 内容的更改。
- 在 Electron 中,进程使用
ipcMain和ipcRenderer模块,通过开发人员定义的“通道”传递消息来进行通信
3.1 render->main : ipcMain.on 与 ipcRenderer.send
主进程
const {app, BrowserWindow, ipcMain} = require('electron')
function handleSetTitle (event, title) {
const webContents = event.sender
const win = BrowserWindow.fromWebContents(webContents)
win.setTitle(title)
}
ipcMain.on('set-title', handleSetTitle)
preload脚本
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
setTitle: (title) => ipcRenderer.send('set-title', title)
})
渲染器进程
使用 window.electronAPI.setTitle()
3.2 render->main : ipcRenderer.invoke 与 ipcMain.handle
主进程
const {app, BrowserWindow, ipcMain} = require('electron')
async function handleFileOpen() {
const { canceled, filePaths } = await dialog.showOpenDialog()
if (canceled) {
return
} else {
return filePaths[0]
}
}
ipcMain.handle('dialog:openFile', handleFileOpen)
preload脚本
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
openFile: () => ipcRenderer.invoke('dialog:openFile')
})
渲染器进程
const filePath = await window.electronAPI.openFile()
console.log(filePath)
3.3 main->render :mainWindow.webContents.send 与ipcRenderer.on
需求: 点击主进程 原生菜单,控制 渲染进程 里的html页面
主进程
const {app, BrowserWindow, Menu, ipcMain} = require('electron')
const path = require('path')
function createWindow () {
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
const menu = Menu.buildFromTemplate([
{
label: app.name,
submenu: [
{
click: () => mainWindow.webContents.send('update-counter', 1),
label: 'Increment',
},
{
click: () => mainWindow.webContents.send('update-counter', -1),
label: 'Decrement',
}
]
}
])
Menu.setApplicationMenu(menu)
mainWindow.loadFile('index.html')
// Open the DevTools.
mainWindow.webContents.openDevTools()
}
app.whenReady().then(() => {
ipcMain.on('counter-value', (_event, value) => {
console.log(value) // will print value to Node console
})
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
ipcMain.on('counter-value', (_event, value) => {
console.log(value) // 将打印到 Node 控制台
})
preload脚本
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
handleCounter: (callback) => ipcRenderer.on('update-counter', callback)
})
渲染器进程
const counter = document.getElementById('counter')
window.electronAPI.handleCounter((event, value) => {
const oldValue = Number(counter.innerText)
const newValue = oldValue + value
counter.innerText = newValue
//发送给主进程
event.sender.send('counter-value', newValue)
})
4.系统本地 API
- 原生Node.js模块由Electron支持,已有对象查看,支持开发原生模块
- 由于Electron具有与给定Node.js不同的
ABI,您使用的原生 模块需要为Electron重新编译 - 使用
node-gyp直接编译
cd /path-to-module/
HOME=~/.electron-gyp node-gyp rebuild --target=1.2.3 --arch=x64 --dist-url=https://electronjs.org/headers
详见 Node addons: juejin.cn/post/710677…
相关文档:
欢迎关注我的前端自检清单,我和你一起成长