”主程序“和“渲染程序”是Electron的兩個核心的概念。
如果你之前做的是浏覽器端JavaScript開發,多程序的概念對你來說可能是一個新的領域。
最初對我來說,這絕對是一個思維方式的轉變,使用多程序可能意味着我們需要在開發過程中做出跟之前不同的設計抉擇。
為什麼Electron具有這種多程序架構?主程序職責是什麼?渲染程序的職責是什麼?它們之間如何實作通信?
首先,我們這裡所說的“程序”是什麼?
一個作業系統級别的程序,或者如Wikipedia所述,它是“正在執行的計算機程式的執行個體”。
我們啟動一個Electron應用程式,然後在macOS中檢查“活動螢幕”,則可以看到與該程式關聯的程序數。

“ Electron”是主程序,一個“ Electron Helper”是GPU程序,另一個“ Electron Helpers”是渲染程序。
這些程序中的每一個彼此并發運作。這裡要記住的最重要的一點是,程序的記憶體和資源是互相隔離的。
舉例來說,假設我有一個子產品,該子產品可以儲存我的主程序和渲染程序所需的某些狀态:
如果我在渲染程序中增加1,則渲染程序中的計數将為1,但在主程序中仍為0。
這兩個程序不共享記憶體或狀态。實際上,該子產品有兩個執行個體在運作。
為什麼要多個程序?
此架構決策源自Chromium。Chromium在單獨的程序中運作每個頁籤(即webContents執行個體),是以,如果一個頁籤遇到緻命錯誤,則不會關閉整個應用程式。從這個意義上說,“ Chromium像作業系統一樣建構,使用多個OS程序将網站彼此隔離,并與浏覽器本身隔離。” 是以,每個程序“在其自己的位址空間中運作,由作業系統排程,并且可以單獨失敗”。當有人一步小心地寫了一個無限循環,然後關閉正在運作的頁籤,而不是整個浏覽器。這種體系結構要感謝這種彈性。還有安全原因。
主程序
主程序負責建立和管理BrowserWindow執行個體以及各種應用程式事件。它還可以執行諸如注冊全局快捷方式,建立系統菜單和對話框,響應自動更新事件等操作。應用程式的入口點将指向将在主程序中執行的JavaScript檔案。主程序以及所有node.js子產品中都提供了一部分Electron API(請參見下圖)。
docs聲明:“基本規則是:如果子產品與GUI或低級系統相關,則它應僅在主程序l中可用。” (請注意,此處的GUI是指本機GUI,而不是Chromium呈現的基于HTML的UI)。這是為了避免潛在的記憶體洩漏問題。
渲染程序
渲染過程負責運作應用程式的使用者界面,換句話說,就是作為webContents執行個體的網頁。渲染程序中提供了所有DOM API,node.js API和Electron API的子集(請參見下圖)。
我曾經将BrowserWindow與渲染程序混合在一起。在視窗中包含webContents執行個體之前,實際上不會建立渲染程序。這有點挑剔,但我認為這很重要,并且知道它會導緻更堅實的概念基礎。另外,一個或多個WebContent可以位于一個視窗中。等等,一個或多個?是的,因為單個視窗可以承載多個Web視圖,并且每個Web視圖都是其自己的webContents執行個體和渲染程序。是以,例如,如果您的頁面中包含2個Web視圖,則将有3個渲染程序-一個用于托管2個Web視圖的父級,然後一個用于每個Web視圖。話雖如此,除非您要運作遠端網頁,否則無需使用Web視圖,是以不必太在意該細節。
上圖顯示了每種程序中可用的Electron API。您還可以看到Node.js API全局可用,而渲染程序中隻有DOM / Browser API。
如何在程序之間進行通信?
Electron使用程序間通信(IPC)在程序之間進行通信-與Chromium相同。
IPC有點像在網頁和iframe或webWorker之間使用postMessage。
一般來說,發送的消息會帶有頻道名稱和一些其他資訊。
IPC可以在渲染程序和主程序之間雙向通信。IPC預設情況下是異步的,但也具有同步API(例如Node.js中的fs)。
Electron還為提供了遠端子產品,例如,您可以使用主處理子產品,就像
Menu
渲染器中可用的一樣。
不需要進行手動IPC調用,但實際上是在幕後,您是通過同步IPC調用向主程序發出指令。
使用devtron子產品,我們可以觀察到使用遠端子產品時發生的所有IPC調用。同步IPC調用可能會有性能缺陷。在許多情況下,可能還不錯。
remote
用于打開對話框時,會發生多個同步IPC調用。
使用ipc或遠端API的代碼非常簡單。
有沒有一個方法是主程序和渲染程序都支援的?
是的,可以通過remote子產品通路主程序API,例如:
IPC是否在底層使用了某些網絡協定(例如tcp,http或更瘋狂的東西)?
不。Chromium的IPC文檔指出,它使用“命名管道”作為IPC的基礎工具。命名管道比網絡協定可以提供更快,更安全的通信。
“命名管道”類似于“無名管道”,當您執行類似操作時使用
ls | grep foo
。命名管道很有趣,
那麼我在哪裡進行CPU密集型工作?
我曾經認為 主程序 是“繁重工作”的理想之地,因為它不會阻塞UI。
實際上,這是錯誤的—如果我們在主程序中執行CPU密集型工作,它将鎖定所有渲染程序。
是以,CPU密集型任務應在單獨的程序中運作-而不是任何包含UI的現有渲染程序或主程序。
其實,最簡單的方法是使用Electron-remote。Electron-remote非常棒,并且具有渲染程序任務池,該任務池将跨多個程序拆分和平衡作業。
這是一個簡單的例子:
在這段代碼中electron remore 最多建立4個BrowserWindow執行個體,所有這些執行個體都需要您的子產品并運作它,然後協調程序之間的來回通信。
附:IPC通信的幾種方式
資料傳輸:一個程序需要将它的資料發送給另一個程序,發送的資料量在一個位元組到幾M位元組之間
共享資料:多個程序想要操作共享資料,一個程序對共享資料的修改,别的程序應該立刻看到。
通知事件:一個程序需要向另一個或一組程序發送消息,通知它(它們)發生了某種事件(如程序終止時要通知父程序)。
資源共享:多個程序之間共享同樣的資源。為了作到這一點,需要核心提供鎖和同步機制。
程序控制:有些程序希望完全控制另一個程序的執行(如Debug程序),此時控制程序希望能夠攔截另一個程序的所有陷入和異常,并能夠及時知道它的狀态改變。
linux常用的程序間的通訊方式
(1)、管道(pipe):管道可用于具有親緣關系的程序間的通信,是一種半雙工的方式,資料隻能單向流動,允許一個程序和另一個與它有共同祖先的程序之間進行通信。
(2)、命名管道(named pipe):命名管道克服了管道沒有名字的限制,同時除了具有管道的功能外(也是半雙工),它還允許無親緣關系程序間的通信。命名管道在檔案系統中有對應的檔案名。命名管道通過指令mkfifo或系統調用mkfifo來建立。
(3)、信号(signal):信号是比較複雜的通信方式,用于通知接收程序有某種事件發生了,除了程序間通信外,程序還可以發送信号給程序本身;linux除了支援Unix早期信号語義函數sigal外,還支援語義符合Posix.1标準的信号函數sigaction(實際上,該函數是基于BSD的,BSD為了實作可靠信号機制,又能夠統一對外接口,用sigaction函數重新實作了signal函數)。
(4)、消息隊列:消息隊列是消息的連結表,包括Posix消息隊列system V消息隊列。有足夠權限的程序可以向隊列中添加消息,被賦予讀權限的程序則可以讀走隊列中的消息。消息隊列克服了信号承載資訊量少,管道隻能承載無格式位元組流以及緩沖區大小受限等缺
(5)、共享記憶體:使得多個程序可以通路同一塊記憶體空間,是最快的可用IPC形式。是針對其他通信機制運作效率較低而設計的。往往與其它通信機制,如信号量結合使用,來達到程序間的同步及互斥。
(6)、記憶體映射:記憶體映射允許任何多個程序間通信,每一個使用該機制的程序通過把一個共享的檔案映射到自己的程序位址空間來實作它。
(7)、信号量(semaphore):主要作為程序間以及同一程序不同線程之間的同步手段。
(8)、套接字(Socket):更為一般的程序間通信機制,可用于不同機器之間的程序間通信。起初是由Unix系統的BSD分支開發出來的,但現在一般可以移植到其它類Unix系統上:Linux和System V的變種都支援套接字。