跳到主要內容

從原生程式碼到 Electron 中的 JavaScript

·閱讀時間 4 分鐘

以 C++ 或 Objective-C 編寫的 Electron 功能如何傳遞到 JavaScript,使其可供終端使用者使用?


背景

Electron 是一個 JavaScript 平台,其主要目的是降低開發人員建置強大桌面應用程式的門檻,而無需擔心特定於平台的實作。然而,Electron 的核心仍然需要以給定的系統語言編寫特定於平台的功能。

實際上,Electron 會為您處理原生程式碼,讓您可以專注於單一 JavaScript API。

但這是如何運作的?以 C++ 或 Objective-C 編寫的 Electron 功能如何傳遞到 JavaScript,使其可供終端使用者使用?

為了追蹤這個路徑,讓我們先從 app 模組 開始。

開啟我們 lib/ 目錄中的 app.ts 檔案,您會在頂端附近找到以下程式碼行

const binding = process.electronBinding('app');

這一行直接指向 Electron 將其 C++/Objective-C 模組繫結到 JavaScript 以供開發人員使用的機制。此函數是由 ElectronBindings 類別的標頭和 實作檔案所建立。

process.electronBinding

這些檔案會新增 process.electronBinding 函數,其行為類似於 Node.js 的 process.bindingprocess.binding 是 Node.js require() 方法的較低層級實作,只不過它允許使用者 require 原生程式碼,而不是其他以 JS 編寫的程式碼。這個自訂的 process.electronBinding 函數授予了從 Electron 載入原生程式碼的能力。

當頂層 JavaScript 模組 (例如 app) 需要此原生程式碼時,該原生程式碼的狀態如何決定和設定?方法在哪裡公開給 JavaScript?屬性呢?

native_mate

目前,此問題的答案可以在 native_mate 中找到:這是 Chromium 的 gin 程式庫的一個分支,它可以更容易地在 C++ 和 JavaScript 之間封送類型。

native_mate/native_mate 內部,有一個用於 object_template_builder 的標頭和實作檔案。這就是讓我們能夠以原生程式碼形成模組的原因,這些模組的形狀符合 JavaScript 開發人員的期望。

mate::ObjectTemplateBuilder

如果我們將每個 Electron 模組視為一個 object,就可以更容易看出為什麼我們要使用 object_template_builder 來建構它們。這個類別建立在 V8 公開的類別之上,V8 是 Google 的開放原始碼高效能 JavaScript 和 WebAssembly 引擎,以 C++ 編寫。V8 實作了 JavaScript (ECMAScript) 規範,因此其原生功能實作可以直接關聯到 JavaScript 中的實作。例如,v8::ObjectTemplate 為我們提供了沒有專用建構函式和原型 JavaScript 物件。它使用 Object[.prototype],在 JavaScript 中相當於 Object.create()

若要查看實際運作情形,請參閱 app 模組的實作檔案 atom_api_app.cc。底部如下

mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
.SetMethod("getGPUInfo", &App::GetGPUInfo)

在上述行中,會在 mate::ObjectTemplateBuilder 上呼叫 .SetMethod。可以在 ObjectTemplateBuilder 類別的任何執行個體上呼叫 .SetMethod,以在 JavaScript 的 Object 原型上設定方法,語法如下

.SetMethod("method_name", &function_to_bind)

這相當於 JavaScript 中的

function App{}
App.prototype.getGPUInfo = function () {
// implementation here
}

此類別還包含在模組上設定屬性的函數

.SetProperty("property_name", &getter_function_to_bind)

.SetProperty("property_name", &getter_function_to_bind, &setter_function_to_bind)

這些將依序是 Object.defineProperty 的 JavaScript 實作

function App {}
Object.defineProperty(App.prototype, 'myProperty', {
get() {
return _myProperty
}
})

function App {}
Object.defineProperty(App.prototype, 'myProperty', {
get() {
return _myProperty
}
set(newPropertyValue) {
_myProperty = newPropertyValue
}
})

可以建立具有開發人員所期望的原型和屬性的 JavaScript 物件,並更清楚地推論在此較低系統層級實作的函數和屬性!

關於在何處實作任何給定模組方法的決定本身是一個複雜且通常不確定的決定,我們將在未來的文章中介紹。