JavaScript is required
Kotae
Touko
3 years ago|views:266

Electron进程间通信

IPC通道

在 Electron 中,进程使用 ipcMain 和 ipcRenderer 模块,通过开发人员定义的“通道”传递消息来进行通信。 这些通道是 任意 (您可以随意命名它们)和 双向 (您可以在两个模块中使用相同的通道名称)的。

模式 1:渲染器进程到主进程(单向)

要将单向 IPC 消息从渲染器进程发送到主进程,您可以使用 ipcRenderer.send API 发送消息,然后使用 ipcMain.on API 接收

通过预加载脚本暴露API,让我们可以在渲染进程发送消息到主进程

preload.js

const {contextBridge, ipcRenderer} = require('electron')

contextBridge.exposeInMainWorld('controlAPI', {
    setScreen: (status) => ipcRenderer.send('set-screen', status),
})

在上面我们暴露了一个 setScreen 方法,我们可以在渲染进程中调用它

index.html

const setScreen = (status: string) => {
  window.controlAPI.setScreen(status)
}

当我们在渲染进程触发 setScreen 方法,在主进程中我们通过 ipcMian.on 接收

mian.js

ipcMain.on('set-screen', (event, args) => {
    switch (args) {
        case 'max':
            win.isMaximized() ? win.unmaximize() : win.maximize()
            break;
        case 'min':
            win.minimize()
            break;
        case 'close':
            win.close()
            break;
        default:
            break;
    }
})

模式 2:主进程到渲染器进程

将消息从主进程发送到渲染器进程时,需要指定是哪一个渲染器接收消息。 消息需要通过其 WebContents 实例发送到渲染器进程。 此 WebContents 实例包含一个 send 方法,其使用方式与 ipcRenderer.send 相同,渲染器进程通过 ipcRenderer.on API接收

通过预加载脚本暴露API,让我们可以在渲染进程监听主进程发送的消息

preload.js

const {contextBridge, ipcRenderer} = require('electron')

contextBridge.exposeInMainWorld('controlAPI', {
    checkUpdate: (callback) => ipcRenderer.on('check-update', callback)
})

在上面我们暴露了一个 checkUpdate 方法,我们可以在渲染进程中调用它

main.js

const sendUpdateMessage = (win, key, value) => {
    win && win.webContents.send('check-update', {key, value})
}

autoUpdater.checkForUpdates();

/*New Update Available*/
autoUpdater.on("update-available", (info) => {
    let pth = autoUpdater.downloadUpdate();
    sendUpdateMessage(win, "update_available", "update_available")
});

autoUpdater.on("update-not-available", (info) => {
    sendUpdateMessage(win, "update_not_available", "update_not_available")
});

/*Download Completion Message*/
autoUpdater.on("update-downloaded", (info) => {
    sendUpdateMessage(win, "update_downloaded", "update_downloaded")
    autoUpdater.quitAndInstall()
    app.quit()
});

autoUpdater.on('download-progress', (progress) => {
    sendUpdateMessage(win, "download_progress", progress.percent)
})

autoUpdater.on("error", (error) => {
    sendUpdateMessage(win, "error", error || 'Error checking for updates')
});

在主进程中我们检测应用更新,检测到什么信息就通过 WebContents.send 发送消息到渲染进程

index.html

window.controlAPI.checkUpdate((event, data) => {
    switch (data.key) {
      case "download_progress":
        percent.value = data.value
        break;
      case "update_available":
        snackbar.info(t('update_available'))
        break;
      case "update_downloaded":
        snackbar.success(t('update_downloaded'))
        break;
      case "error":
        snackbar.error(data.value)
        break;
      default:
        break;
    }
  })

在渲染进程我们通过预加载脚本暴露的API监听着主进程发送过来的消息并执行操作

模式 3:渲染器进程到主进程(双向)

双向 IPC 的一个常见应用是从渲染器进程代码调用主进程模块并等待结果。 这可以通过将 ipcRenderer.invoke 与 ipcMain.hendle 搭配使用来完成。

preload.js

const { contextBridge, ipcRenderer } = require('electron/renderer')

contextBridge.exposeInMainWorld('electronAPI', {
  openFile: () => ipcRenderer.invoke('dialog:openFile')
})

在预加载脚本中,我们暴露了一个单行的 openFile 函数,它调用并返回 ipcRenderer.invoke('dialog:openFile') 的值

main.js

async function handleFileOpen () {
  const { canceled, filePaths } = await dialog.showOpenDialog()
  if (!canceled) {
    return filePaths[0]
  }
}
app.whenReady().then(() => {
  ipcMain.handle('dialog:openFile', handleFileOpen)
  createWindow()
  app.on('activate', function () {
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

在主进程中,我们将创建一个 handleFileOpen 函数,它调用 dialog.showOpenDialog 并返回用户选择的文件路径值。 每当渲染器进程通过 dialog:openFile 通道发送 ipcRenderer.invoke 消息时,此函数被用作一个回调。 然后,返回值将作为一个 Promise 返回到最初的 invoke 调用。

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <title>Dialog</title>
  </head>
  <body>
    <button type="button" id="btn">Open a File</button>
    File path: <strong id="filePath"></strong>
    <script src='./renderer.js'></script>
  </body>
</html>

用户界面包含一个 #btn 按钮元素,将用于触发我们的预加载 API,以及一个 #filePath 元素,将用于显示所选文件的路径

renderer.js

const btn = document.getElementById('btn')
const filePathElement = document.getElementById('filePath')

btn.addEventListener('click', async () => {
  const filePath = await window.electronAPI.openFile()
  filePathElement.innerText = filePath
})

在上面的代码片段中,我们监听 #btn 按钮的点击,并调用 windos.electronAPI.openFile() API 来激活原生的打开文件对话框。 然后我们在 #filePath 元素中显示选中文件的路径

tags:

electron
0
comments:
It's deserted here, it looks like no one's been here