进程之间的通信

主进程向渲染进程通信

  • 主进程发送 webContents.send( channel , ...args )

  • 渲染接收 ipcRenderer.on( channel , handler )

    实例如下:

// main
let win = null;
app.on("ready", () => {
  win = new BrowserWindow({
    width: 1200,
    height: 800
  });
  win.webContents.send("action", "new");
});
// renderer
import { ipcRenderer } from "electron";
ipcRenderer.on("action", arg => {
  // do somethig
});

渲染进程向主进程通信

  • Callback的写法

    • ipcRenderer.send(channel,..args)
    • ipMain.on(channel,listener)
  • Promise的写法(Electron7.0 后,请求处理+响应模式)

    • ipcRenderer.invoke(channel,..args)
    • ipMain.handle(channel,listener)

渲染进程之间的通信

页面间(渲染进程于渲染进程之间的通信)准要在一下两种场景中存在:

  • 通知事件
    • ipcRenderer.sendTo()
  • 数据共享
    • web 技术(storage, indexDB)
    • remote

首先需要获得另一个子窗口的id(webContentsId)

我们在 main.js 里面把子窗口的 id 通过一个对象存储起来,然后在渲染进程里面我们可以获取使用:

// main
let win;
let win2;
app.on("ready", () => {
  win = new BrowserWindow({
    width: 300,
    height: 300,
    webPreferences: {
      enableRemoteModule: true,
      nodeIntegration: true
    }
  });
  win.loadFile("./index.html");

  win2 = new BrowserWindow({
    width: 300,
    height: 300,
    webPreferences: {
      enableRemoteModule: true,
      nodeIntegration: true
    }
  });
  win2.loadFile("./index2.html");
  global.sharedObject = {
    win2WebContentsId: win2.webContents.id
  };
});
// index.js
let sharedObject = remote.getGlobal("sharedObject");
let win2WebContentsId = sharedObject.win2WebContentsId;

ipcRenderer.sendTo(win2WebContentsId, "do-some-work", 1);
// index2.js
ipcRenderer.on("do-some-work", (e, a) => {
  alert("renderer2 handle some work" + a);
});

ipcMain

从主进程到渲染进程的异步通信。

ipcMain 是一个 EventEmitter 的实例。 当在主进程中使用时,它处理从渲染器进程(网页)发送出来的异步和同步信息。 从渲染器进程发送的消息将被发送到该模块。

发送消息

主进程向渲染进程发送消息,查阅webContents.send获取更多信息。

  • 发送时间时,时间名称为channel

  • 回复同步消息时,通过event.returnValue

  • 可用通过event.reply(...)将异步消息发送至发送者。

下面是在渲染和主进程之间发送和处理消息的一个例子:

// 在主进程中.
const { ipcMain } = require("electron");
ipcMain.on("asynchronous-message", (event, arg) => {
  console.log(arg); // prints "ping"
  event.reply("asynchronous-reply", "pong");
});

ipcMain.on("synchronous-message", (event, arg) => {
  console.log(arg); // prints "ping"
  event.returnValue = "pong";
});
//在渲染器进程 (网页) 中。
const { ipcRenderer } = require("electron");
console.log(ipcRenderer.sendSync("synchronous-message", "ping")); // prints "pong"

ipcRenderer.on("asynchronous-reply", (event, arg) => {
  console.log(arg); // prints "pong"
});
ipcRenderer.send("asynchronous-message", "ping");

方法

IpcMain 模块有以下方法来侦听事件:

ipcMain.handle(channel,listener)

  • channel String
  • listener Function <Promise any>
    • event IpcMainInvokeEvent
    • ...args any[]

::: tip ipcRender.invoke( ) 和 ipcMain.handle( ) 需要配套使用 :::

示例如下:

// renderer
async function notification() {
  const res = await ipcRenderer.invoke("notification");
  if (res === "rest") {
  } else if (res === "work") {
  }
}
// main
ipcMain.handle("notification", async () => {
  let result = await new Promise(resolve => {
    // do something
    resolve("work");
  });
  return result;
});

ipcRenderer

从渲染器进程到主进程的异步通信。

ipcRenderer 是一个 EventEmitter的实例。 你可以使用它提供的一些方法从渲染进程 (web 页面) 发送同步或异步的消息到主进程。 也可以接收主进程回复的消息。

方法

ipcRenderer 模块使用以下方法来监听事件和发送消息:

ipcRenderer.invoke(channel, ..args)

  • channel String
  • ...args any[]

返回 Promise<any> - Resolves with the response from the main process.

ipcRenderer.sendTo(webContentsId, channel, ...args)

  • webContentsId Number
  • channel String
  • ...args any[]

向指定的子窗口发送某个事件

desktopCapturer

通过[navigator.mediaDevices.getUserMedia] API ,可以访问那些用于从桌面上捕获音频和视频的媒体源信息。

捕获桌面流

下面的示例演示如何从标题为 Electron 的桌面窗口捕获视频:

// renderer process
onMounted(async () => {
  const sources = await desktopCapturer.getSources({ types: ["screen"] });
  navigator.webkitGetUserMedia(
    {
      audio: false,
      video: {
        mandatory: {
          chromeMediaSource: "desktop",
          chromeMediaSourceId: sources[0].id,
          minWidth: 1280,
          maxWidth: 1280,
          minHeight: 720,
          maxHeight: 720
        }
      }
    },
    stream => {
      play(stream);
    },
    err => {
      console.error(err);
    }
  );
});

const play = stream => {
  const oVideo = video.value; //dom
  oVideo.srcObject = stream;
  oVideo.onloadedmetadata = () => {
    oVideo.play();
  };
};

::: tip

  • 若要从 desktopCapturer 提供的源捕获视频, 则传递给 [navigator.mediaDevices.getUserMedia] 的约束必须包括 chromeMediaSource: "desktop"audio: false

  • 要从整个桌面同时捕获音频和视频, 传递给 [navigator.mediaDevices.getUserMedia] 的约束必须包括 chromeMediaSource: ‘ desktop ‘, 同时用于 audio 和 video, 但不应包括 chromeMediaSourceId 约束。 :::

  audio: {
    mandatory: {
      chromeMediaSource: 'desktop'
    }
  },
  video: {
    mandatory: {
      chromeMediaSource: 'desktop'
    }
  }
}

录屏

::: tip 首先通过desktopCapturer获取桌面的视频和音频流,然后传递给MediaRecorder进行录制,结束录制时保存视频文件 :::

<button @click="start">start</button><br />
<button @click="stop">stop</button>

捕获桌面流

let recorder;
const start = async () => {
  const sources = await desktopCapturer.getSources({ types: ["screen"] });
  navigator.webkitGetUserMedia(
    {
      audio: false,
      video: {
        mandatory: {
          chromeMediaSource: "desktop",
          chromeMediaSourceId: sources[0].id,
          minWidth: 1280,
          maxWidth: 1280,
          minHeight: 720,
          maxHeight: 720
        }
      }
    },
    stream => {
      record(stream);
    },
    err => {
      console.error(err);
    }
  );
};

开始记录

const record = stream => {
  recorder = new MediaRecorder(stream);
  recorder.start();
  recorder.ondataavailable = event => {
    const blob = new Blob([event.data], {
      type: "video/mp4"
    });
    saveMedia(blob);
  };

  recorder.onerror = err => {
    console.error(err);
  };
};

点击结束录制的时候 通过recorder.stop()会触发recorder.ondataavailable()方法。当我们拿到文件流对象的时候后就可以进行保存。

const saveMedia = blob => {
  const reader = new FileReader();
  reader.onload = () => {
    const buffer = new Buffer(reader.result);
    const index = Math.random() * 10;
    fs.writeFile(
      `C:\\Users\\Administrator\\Desktop\\${index}.mp4`,
      buffer,
      (err, res) => {
        if (err) {
          console.error(err);
          return;
        } else {
          alert("save success");
        }
      }
    );
  };
  reader.readAsArrayBuffer(blob);
};

const stop = () => {
  recorder.stop();
};