Electron 內部原理:將 Node 作為函式庫使用
這是說明 Electron 內部原理的持續系列文章中的第二篇。如果您還沒有看過關於事件迴圈整合的第一篇文章,請先查看。
大多數人將Node用於伺服器端應用程式,但由於 Node 豐富的 API 集和蓬勃發展的社群,它也非常適合作為嵌入式函式庫。這篇文章說明如何在 Electron 中將 Node 作為函式庫使用。
建置系統
Node 和 Electron 都使用 GYP
作為其建置系統。如果要在應用程式內嵌入 Node,您也必須將它用作建置系統。
不熟悉 GYP
?請在繼續閱讀這篇文章之前,先閱讀本指南。
Node 的標誌
Node 原始碼目錄中的 node.gyp
檔案說明了如何建置 Node,以及許多 GYP
變數,這些變數控制啟用了 Node 的哪些部分以及是否開啟某些組態。
若要變更建置標誌,您需要在專案的 .gypi
檔案中設定變數。Node 中的 configure
指令碼可以為您產生一些常見的組態,例如執行 ./configure --shared
將產生一個 config.gypi
,其中包含指示 Node 作為共用函式庫建置的變數。
Electron 不使用 configure
指令碼,因為它有自己的建置指令碼。Node 的組態定義在 Electron 根原始碼目錄中的 common.gypi
檔案中。
將 Node 與 Electron 連結
在 Electron 中,透過將 GYP
變數 node_shared
設定為 true
來將 Node 作為共用函式庫連結,因此 Node 的建置類型將從 executable
變更為 shared_library
,並且不會編譯包含 Node main
進入點的原始碼。
由於 Electron 使用 Chromium 隨附的 V8 函式庫,因此不會使用 Node 原始碼中包含的 V8 函式庫。這是透過將 node_use_v8_platform
和 node_use_bundled_v8
都設定為 false
來完成的。
共用函式庫或靜態函式庫
在與 Node 連結時,有兩個選項:您可以將 Node 建置為靜態函式庫並將其包含在最終可執行檔中,或者您可以將其建置為共用函式庫並與最終可執行檔一起運送。
在 Electron 中,Node 長期以來都是作為靜態函式庫建置的。這使得建置變得簡單,能夠實現最佳的編譯器最佳化,並允許 Electron 在沒有額外 node.dll
檔案的情況下發佈。
然而,在 Chrome 切換為使用 BoringSSL 後,情況發生了變化。BoringSSL 是 OpenSSL 的分支,它移除了幾個未使用的 API 並變更了許多現有的介面。因為 Node 仍然使用 OpenSSL,如果它們連結在一起,編譯器會由於符號衝突而產生許多連結錯誤。
Electron 無法在 Node 中使用 BoringSSL,或者在 Chromium 中使用 OpenSSL,因此唯一的選擇是切換為將 Node 建置為共用函式庫,並在每個元件中隱藏 BoringSSL 和 OpenSSL 符號。
此變更為 Electron 帶來了一些正面的副作用。在此變更之前,如果您使用原生模組,則無法在 Windows 上重新命名 Electron 的可執行檔,因為可執行檔的名稱已硬式編碼在匯入函式庫中。在將 Node 建置為共用函式庫後,此限制已消失,因為所有原生模組都已連結到 node.dll
,而 node.dll
的名稱不需要變更。
支援原生模組
Node 中的原生模組運作方式是定義一個 Node 要載入的進入函式,然後從 Node 中搜尋 V8 和 libuv 的符號。這對嵌入者來說有點麻煩,因為預設情況下,當 Node 作為函式庫建置時,V8 和 libuv 的符號會被隱藏,而原生模組會因為找不到符號而載入失敗。
因此,為了使原生模組能夠運作,V8 和 libuv 的符號在 Electron 中被暴露出來。對於 V8,這是透過強制暴露 Chromium 組態檔中的所有符號來實現的。對於 libuv,則是透過設定 BUILDING_UV_SHARED=1
定義來實現的。
在您的應用程式中啟動 Node
在完成所有建置和連結 Node 的工作之後,最後一步是在您的應用程式中執行 Node。
Node 並未提供許多將自身嵌入到其他應用程式中的公開 API。通常,您只需呼叫node::Start
和 node::Init
來啟動一個新的 Node 實例。但是,如果您正在建置一個基於 Node 的複雜應用程式,則必須使用 node::CreateEnvironment
之類的 API 來精確控制每個步驟。
在 Electron 中,Node 以兩種模式啟動:在主進程中運行的獨立模式,這類似於官方的 Node 二進制檔;以及將 Node API 插入到網頁中的嵌入模式。詳細資訊將在未來的一篇文章中解釋。