跳到主要內容

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 物件,正如開發人員所期望的那樣,並且可以更清楚地推理在這個較低系統層級實作的函數和屬性!

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