Electron-handbook

/post/electron-handbook article cover image

项目模版

对于electron无论是webpack、vite都需要分别对mainpreloadrenderer进行打包并和chromium、node api生成对应平台应用

  1. electron-forge : 通过其插件系统提供了项目环境、打包分发、包发布、等完整流程,并且具有webpack、vite两种构建可以选择
  2. electron-vite : 以vite为构建方案的不同框架模版

核心api

main-process

主进程主要提供作用到操作系统的api及事件,通常位于main工程目录下

  • app : 包含了主应用程序事件触发及监听
ts
app.on('window-all-closed', () => {
  app.quit();
})
  • BrowserWindow : 主应用窗口相关(配置、页面渲染、调试工具、窗口事及监听)
    • 一个应用可以存在多个窗口但需要指明父级窗口
ts
function createWindow() {
  mainWIndow = new BrowserWindow({ /* ... */ })

  secondaryWindow = new BrowserWindow({
    // ...,
    modal: false, // 是否以modal形式展示
    show: false, // modal为true时生效
    parent: mainWindow
  });
}
  • webContents : 作为BrowserWindow的属性(对应的配置也将反映到属性当中)用于响应和控制web页面的事件触发及监听
    • mainWindow.webContents
ts
const wc = mmainWindow.webContents;
wc.on('did-finish-load', () => {})
wc.on('dom-ready', () => {})
wc.on('focus', () => {})
// ...
ts
globalShortcut.register('CommandOrControl+Shift+Alt+C', () => {
  app.focus();
  browserWindow.show();
  browserWindow.focus();
});
  • Menu : 菜单相关配置(Menu、MenuItem、context-menu)
ts
const mainMenu = new Menu();
const menuItem = new MenuIitem({ label: 'File': subMenu: [/**/] })
mainMenu.append(menuItem)
Menu.setApplicationMenu(mainMenu)

const contextMenu =  Menu.buildFromTemplate({ label: 'xxx', subMenu: [/**/]})
mainWindow.webContents.on('context-menu', e => contextMenu.popup())
  • Tray : 系统托盘图标菜单配置
ts
const someTray = new Tray('xxx/xxx/icon.png')
someTray.setToolTip('hover tip text')
someTray.on('click', e => {
  // do something
})
  • screen : 屏幕相关事件及方法(尺寸、显示、光标位置)
ts
const [dp1, dp2] = screen.getAllDisplays()
console.log(`
  primary monitor: ${dp1.size.width}x${dp1.size.height}\n
  srcondary monitor: ${dp2.size.width}x${dp2.size.height}`
)
screen.on('display-metrics-changed', (e, display, metricsChanged) => {
  console.log(metricsChanged)
})

renderer-process

渲染进程主要包含mainwindow中渲染的web相关页面及资源,通常位于renderer工程目录下.新版本不能直接在渲染进程中使用必须通过preload桥接

  • clipboard : 系统粘贴板相关
  • contextBridge : 通过ipcRenderer通信暴露桥接主线程与渲染线程的事件,通常位于preload
  • crashRepoeter : 自动提交崩溃报告到远端服务器
  • ipcRenderer : 用于主进程和渲染进程之间监听和派发约定事件
  • webFrame : 用于自定义渲染当前的web页面(zooom、insertCSS、insetText、executeJavascript、...)
  • desktopCapturer : 桌面音视频捕捉

ipcinter-process-communication

<Primary>IPC</Primary>主要用于ipcMainipcRenderer之间的通信

  • Renderer to main : 渲染进程到主进程,出了send > on这种通用订阅方式还有invoke > handle的异步返回方式
ts
import { contextBridge, ipcMain, ipcRenderer } from "electron"

// main.ts
ipcMain.on('someContent', (e, content: string) => console.log(content))
ipcMain.handle('otherContent', async (e, content: string) => dealWithContent(content))

// preload.ts
contextBridge.exposeInMainWorld('app', {
  sendToMain: (content: string) => {
    ipcRenderer.send('someContent', content)
  },
  otherToMain: async (content: stirng) => {
    const result = await ipcRenderer.invoke('otherContent')
    return result
  }
})

// renderer/index.ts
document.getElementById('dom1').onclick = () => {
  window.app.sendToMain()
}
document.getElementById('dom2').onclick = () => {
  window.app.otherToMain()
}
ts
import { BrowserWindow, ipcMain, ipcRenderer } from "electron"

// main.ts
const mainWindow = new BrowserWindow(/* ... */)

Menu.buildFromTemplate([
  {
    label: 'xxx'
    subMenu: [
      {
        label: 'xxx',
        click: () => mainWindow.webContents.send('updateSomething', 'xxx')
      },
      // ...
    ]
  }
])

// preload.ts
// 这里可以不暴露到renderer中,直接定义在preload中
window.addEventListener('DOMContentLoaded', () => {
  const contentDom = document.getElementById('dom')
  ipcRenderer.on('updateSomthing', (_event, content) => {
    contentDom.innerText = content
  })
})

// index.html
<body>
  <div id="dom">/* ... */</div>
</body>

<script type="module" src="renderer/index.ts">

shared-modules

  • process : 包含了可以在主、渲染进程中访问使用的属性、方法、事件
  • shell : 包含了与桌面集成相关的功能(openPathshowItemInFolder、...,不同操作系统的表现也不相同)
  • nativeImage : 图片相关api可以通过本机路径、nativeImage实例创建并包含很多图片实例方法(toPNGtoJPEGtoDataURL、...)
  • clipboard : 粘贴板相关功能

扩展功能

  • Offsreen Rendering : 使用禁止硬件加速开启离屏渲染(不渲染页面的情况下进行逻辑处理)
ts
import { app, BrowserWindow } from "electron"

app.disableHardwareAcceleration()

const mainWindow = new BrowserWindow({
  show: false,
  webPreferences: {
    nodeIntegration: true,
    offscreen: true
  }
}})

mainWindow.loadURL('https://electronjs.org')
let i = 1
mainWindow.webContents.on('paint', (e, dirty, image) => {
  fs.writeFile(app.getPath('desktop') + `/screenshot_${i}`, image.toPNG(), console.log)
  i++;
})
mainWindow.webContents.on('did-finish-load', () => {
  // ...
})