4.10.electron

504 阅读3分钟

1.概念

  • 官网: www.electronjs.org/zh/docs/lat…

  • Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架,vscode就是使用它开发的

  • Chromium 、Node.js 和用于调用系统本地功能的 API 三大板块

image.png

2 多进程

image.png

2.1 主进程

  • 每个 Electron 应用都有一个单一的主进程,作为应用程序的入口点。
  • 主进程在 Node.js 环境中运行,这意味着它具有 require 模块和使用所有 Node.js API 的能力
  1. 窗口管理
    主进程的主要目的是使用 BrowserWindow 模块创建和管理应用程序窗口
  2. 控制应用程序的生命周期
  3. 控制原生桌面功能

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…

相关文档:

欢迎关注我的前端自检清单,我和你一起成长