electron监听网页_Electron 进程通信
本文作者:IMWeb laynechen
未經(jīng)同意,禁止轉(zhuǎn)載
Electron 中的進(jìn)程分類
在 Electron 中,存在兩種進(jìn)程:主進(jìn)程和渲染進(jìn)程。
主進(jìn)程 (Main Process)
一個 Electron 應(yīng)用只有 一個主進(jìn)程
當(dāng)我們執(zhí)行 electron . 命令后, Electron 會運行當(dāng)前目錄(.)下的 package.json 文件中 main 字段指定的文件。而運行該文件的進(jìn)程既是主進(jìn)程。
運行在主進(jìn)程中的腳本可以通過創(chuàng)建一個窗口,并傳入 URL,讓這個窗口加載一個網(wǎng)頁來展示圖形界面。
與創(chuàng)建 GUI 相關(guān)的接口只應(yīng)該由主進(jìn)程來調(diào)用。
渲染進(jìn)程 (Renderer Process)
在Electron里的每個頁面都有它自己的進(jìn)程,叫作渲染進(jìn)程。主進(jìn)程通過實例化 BrowserWindow,每個 BrowserWindow 實例都在它自己的渲染進(jìn)程內(nèi)返回一個 web 頁面。當(dāng) BrowserWindow 實例銷毀時,相應(yīng)的渲染進(jìn)程也會終止。
渲染進(jìn)程由主進(jìn)程進(jìn)行管理。每個渲染進(jìn)程都是相互獨立的,它們只關(guān)心自己所運行的 web 頁面。
問題
這篇文章主要要解決的問題是:
Electron 與 View 層(網(wǎng)頁),也就是主進(jìn)程與渲染進(jìn)程是如何進(jìn)行通信的?
不同的通信是如何實現(xiàn)的?
先解決第一個問題。
Electron 與 View 層(網(wǎng)頁)是如何進(jìn)行通信的?
Electron 提供了兩種通信方法:
1. 利用 ipcMain 和 ipcRenderer 模塊
官方文檔 上有使用這兩個模塊進(jìn)行進(jìn)程通信的例子:
// In main process.
const {ipcMain} = require('electron')
ipcMain.on('asynchronous-message', (event, arg) => {
console.log(arg) // prints "ping"
event.sender.send('asynchronous-reply', 'pong')
})
ipcMain.on('synchronous-message', (event, arg) => {
console.log(arg) // prints "ping"
event.returnValue = 'pong'
})
// In renderer process (web page).
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')
渲染進(jìn)程可以通過 ipcRenderer 模塊的 send 方法向主進(jìn)程發(fā)送消息。在主進(jìn)程中,通過 ipcMain 模塊設(shè)置監(jiān)聽 asynchronous-message 和 synchronous-message 兩個事件,當(dāng)渲染進(jìn)程發(fā)送時就可以針對不同的事件進(jìn)行處理。
主進(jìn)程監(jiān)聽事件的回調(diào)函數(shù)中,會傳遞 event 對象及 arg 對象。arg 對象中保存渲染進(jìn)程傳遞過來的參數(shù)。通過 event.sender 對象,主進(jìn)程可以向渲染進(jìn)程發(fā)送消息。如果主進(jìn)程執(zhí)行的是同步方法,還可以通過設(shè)置 event.returnValue 來返回信息。
上面說了渲染進(jìn)程如何向主進(jìn)程發(fā)送消息,但主進(jìn)程也可以主動向渲染進(jìn)程發(fā)送消息
在主進(jìn)程中,我們會創(chuàng)建一個 BrowserWindow 對象,這個對象有 webContents 屬性。webContets 提供了 send 方法來實現(xiàn)向渲染進(jìn)程發(fā)送消息。當(dāng)然 webContents 對象遠(yuǎn)不止這兩個通信方法,具體可以看 webContents
下面是官方文檔提供的使用 webContents 實現(xiàn)通信的例子:
// In the main process.
const {app, BrowserWindow} = require('electron')
let win = null
app.on('ready', () => {
win = new BrowserWindow({width: 800, height: 600})
win.loadURL(`file://${__dirname}/index.html`)
win.webContents.on('did-finish-load', () => {
win.webContents.send('ping', 'whoooooooh!')
})
})
console.log(message) // Prints 'whoooooooh!'
})
注意,webContents.on 監(jiān)聽的是已經(jīng)定義好的事件,如上面的 did-finish-load。要監(jiān)聽自定義的事件還是通過 ipcMain 和 ipcRenderer。
渲染進(jìn)程的監(jiān)聽事件回調(diào)函數(shù)中,也可以通過 event.sender 來向主進(jìn)程發(fā)送消息。這個對象只是 ipcRenderer 的引用(event.sender === ipcRenderer)。因此,event.sender 發(fā)送的消息在主進(jìn)程中還是需要通過 ipcMain.on 方法來監(jiān)聽,而不是通過 webContents.on 方法。
2. 利用 electron.remote 模塊
在渲染進(jìn)程中,可以通過
const { remote } = require('electron');
獲取到 remote 對象,通過 remote 對象可以讓渲染進(jìn)程訪問/使用主進(jìn)程的模塊。例如,通過 remote 在渲染進(jìn)程中新建一個窗口:
const {BrowserWindow} = require('electron').remote
let win = new BrowserWindow({width: 800, height: 600})
win.loadURL('https://github.com')
同樣的,我們也可以通過 remote 對象訪問到 app 對象。這樣我們就可以訪問到我們在主進(jìn)程中掛載到 electron.app 對象上的方法。
如:
main.js 文件:
// In main process
const { app } = require('electron');
const utils = require('./utils');
app.utils = utils; // 將在 Electron 層實現(xiàn)的接口綁定到 app 上
index.js 文件(被網(wǎng)頁引用的腳本文件):
const { remote } = require('electron');
// In renderer process
function(){
// remote.app.utils 對象與上述文件中的 utils 對象是一樣的。
remote.app.utils.test();
}
Electron 的兩種進(jìn)程通信方法是如何實現(xiàn)的?
知道怎么用還不夠,還需要了解 Electron 是如何實現(xiàn)這兩種通信方法的,以及 Electron 為什么要實現(xiàn)兩種通信方法,這兩種通信方法的有什么不同的地方。弄清楚這些開發(fā)起來才會對程序的數(shù)據(jù)流比較清晰。
ipcMain 和 ipcRenderer
The ipcMain module is an instance of the EventEmitter class. When used in the main process, it handles asynchronous and synchronous messages sent from a renderer process (web page). Messages sent from a renderer will be emitted to this module.
ipcMain 和 ipcRenderer 都是 EventEmitter 類的一個實例。而 EventEmitter 類由 NodeJS 中的 events 模塊導(dǎo)出。
events.EventEmitter
EventEmitter 類是 NodeJS 事件的基礎(chǔ),實現(xiàn)了事件模型需要的接口, 包括 addListener,removeListener, emit 及其它工具方法. 同原生 JavaScript 事件類似, 采用了發(fā)布/訂閱(觀察者)的方式, 使用內(nèi)部 _events 列表來記錄注冊的事件處理器。
我們通過 ipcMain和ipcRenderer 的 on、send 進(jìn)行監(jiān)聽和發(fā)送消息都是 EventEmitter 定義的相關(guān)接口。
那么 ipcMain 和 ipcRenderer 是如何實現(xiàn)這些接口的呢?
ipc-renderer.js
const binding = process.atomBinding('ipc')
...
// Created by init.js.
const ipcRenderer = v8Util.getHiddenValue(global, 'ipc')
ipcRenderer.send = function (...args){
return binding.send('ipc-message', args)
}
....
module.exports = ipcRenderer
調(diào)用了 atomBinding('ipc') 得到的 binding 對象的 send 方法。能力有限,就分析到這。后面 binding.send 應(yīng)該就是 IPC 相關(guān)的實現(xiàn)了:對傳送的數(shù)據(jù)進(jìn)行序列化和反序列化。
// 主進(jìn)程
ipcMain.on('test1', (e) => {
const obj = {};
obj.toJSON = () => 'call toJSON';
e.returnValue = obj;
})
ipcMain.on('test2', (e) => {
const obj = { name: '123' };
e.returnValue = obj;
})
// 渲染進(jìn)程
let returnValue = ipcRenderer.sendSync('test1');
console.log(typeof returnValue, returnValue); // 'string call toJSON'
returnValue = ipcRenderer.sendSync('test2');
console.log(typeof returnValue, returnValue); // 'object Object name: "123"__proto__: Object'
從渲染進(jìn)程輸出的消息可以看到,主進(jìn)程將返回值調(diào)用 toJSON 后傳遞給渲染進(jìn)程。渲染進(jìn)程再對傳輸過來的內(nèi)容進(jìn)行反序列化。
remote 遠(yuǎn)程對象
通過 remote 對象,我們可以不必發(fā)送進(jìn)程間消息來進(jìn)行通信。但實際上,我們在調(diào)用遠(yuǎn)程對象的方法、函數(shù)或者通過遠(yuǎn)程構(gòu)造函數(shù)創(chuàng)建一個新的對象,實際上都是在發(fā)送一個同步的進(jìn)程間消息(官方文檔 上說這類似于 JAVA 中的 RMI)。
也就是說,remote 方法只是不用讓我們顯式的寫發(fā)送進(jìn)程間的消息的方法而已。在上面通過 remote 模塊創(chuàng)建 BrowserWindow 的例子里。我們在渲染進(jìn)程中創(chuàng)建的 BrowserWindow 對象其實并不在我們的渲染進(jìn)程中,它只是讓主進(jìn)程創(chuàng)建了一個 BrowserWindow 對象,并返回了這個相對應(yīng)的遠(yuǎn)程對象給了渲染進(jìn)程。
參考資料
總結(jié)
以上是生活随笔為你收集整理的electron监听网页_Electron 进程通信的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LS命令跑小火车
- 下一篇: 数仓 DW层中主题表之页面交互事件概况主