跳至主要內容

程序模型

Electron 從 Chromium 繼承了其多程序架構,這使得該框架在架構上與現代網路瀏覽器非常相似。本指南將擴展 教學 中應用的概念。

為什麼不使用單一程序?

網路瀏覽器是非常複雜的應用程式。除了顯示網路內容的主要功能之外,它們還有許多次要責任,例如管理多個視窗(或索引標籤)和載入第三方擴充功能。

在早期,瀏覽器通常使用單一程序來處理所有這些功能。雖然這種模式意味著每個開啟的索引標籤的額外負擔較少,但也意味著一個網站當機或凍結會影響整個瀏覽器。

多程序模型

為了解決這個問題,Chrome 團隊決定讓每個索引標籤都在自己的程序中渲染,限制了網頁上的錯誤或惡意程式碼可能對整個應用程式造成的危害。然後,單一瀏覽器程序會控制這些程序,以及整個應用程式生命週期。以下來自 Chrome Comic 的圖表視覺化了此模型

Chrome's multi-process architecture

Electron 應用程式的結構非常相似。作為應用程式開發人員,您可以控制兩種程序類型:主要渲染。這些類似於上述 Chrome 自己的瀏覽器和渲染程序。

主要程序

每個 Electron 應用程式都有一個主要程序,它充當應用程式的進入點。主要程序在 Node.js 環境中執行,這表示它有能力 require 模組並使用所有 Node.js API。

視窗管理

主要程序的主要目的是使用 BrowserWindow 模組建立和管理應用程式視窗。

每個 BrowserWindow 類別的實例都會建立一個應用程式視窗,該視窗會在單獨的渲染程序中載入網頁。您可以使用視窗的 webContents 物件,從主要程序與此網路內容互動。

main.js
const { BrowserWindow } = require('electron')

const win = new BrowserWindow({ width: 800, height: 1500 })
win.loadURL('https://github.com')

const contents = win.webContents
console.log(contents)

注意:也會為 網頁嵌入(例如 BrowserView 模組)建立渲染程序。嵌入式網頁內容也可以存取 webContents 物件。

由於 BrowserWindow 模組是 EventEmitter,您也可以為各種使用者事件新增處理常式(例如,最小化或最大化您的視窗)。

BrowserWindow 實例被銷毀時,其對應的渲染程序也會終止。

應用程式生命週期

主要程序還透過 Electron 的 app 模組控制您的應用程式生命週期。此模組提供大量事件和方法,可用於新增自訂應用程式行為(例如,以程式設計方式關閉您的應用程式、修改應用程式停靠區或顯示「關於」面板)。

作為實際範例,快速入門指南中顯示的應用程式使用 app API 來建立更原生的應用程式視窗體驗。

main.js
// quitting the app when no windows are open on non-macOS platforms
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})

原生 API

為了將 Electron 的功能擴展到不僅僅是網路內容的 Chromium 包裝器,主要程序還新增了自訂 API 來與使用者的作業系統互動。Electron 公開了各種模組,這些模組可控制原生桌面功能,例如選單、對話方塊和系統匣圖示。

如需 Electron 主要程序模組的完整清單,請查看我們的 API 文件。

渲染程序

每個 Electron 應用程式都會為每個開啟的 BrowserWindow(以及每個網頁嵌入)產生一個單獨的渲染程序。顧名思義,渲染器負責渲染網路內容。對於所有意圖和目的,在渲染程序中執行的程式碼應根據網路標準運作(至少在 Chromium 允許的範圍內)。

因此,單一瀏覽器視窗中的所有使用者介面和應用程式功能都應使用您在網路上使用的相同工具和範例來編寫。

雖然本指南無法解釋每個網路規格,但要理解的最低限度是

  • HTML 檔案是渲染程序的進入點。
  • 使用者介面樣式是透過階層式樣式表 (CSS) 新增的。
  • 可執行 JavaScript 程式碼可以透過 <script> 元素新增。

此外,這也表示渲染器無法直接存取 require 或其他 Node.js API。為了直接在渲染器中包含 NPM 模組,您必須使用與在網路上使用的相同的綁定器工具鏈(例如,webpackparcel)。

警告

為了方便開發,渲染程序可以產生完整的 Node.js 環境。過去,這曾經是預設值,但基於安全性考量,此功能已停用。

此時,您可能想知道如果這些功能只能從主要程序存取,那麼渲染程序使用者介面如何與 Node.js 和 Electron 的原生桌面功能互動。事實上,沒有直接的方法可以匯入 Electron 的內容指令碼。

預載指令碼

預載指令碼包含在網路內容開始載入之前在渲染程序中執行的程式碼。這些指令碼在渲染器內容中執行,但透過存取 Node.js API 而被授予更多權限。

預載指令碼可以附加到 BrowserWindow 建構函式的 webPreferences 選項中的主要程序。

main.js
const { BrowserWindow } = require('electron')
// ...
const win = new BrowserWindow({
webPreferences: {
preload: 'path/to/preload.js'
}
})
// ...

由於預載指令碼與渲染器共用全域 Window 介面,並且可以存取 Node.js API,因此它可透過在您的網路內容可以使用的 window 全域中公開任意 API 來增強您的渲染器。

雖然預載指令碼與其附加的渲染器共用 window 全域,但由於 contextIsolation 預設值,您無法直接將任何變數從預載指令碼附加到 window

preload.js
window.myAPI = {
desktop: true
}
renderer.js
console.log(window.myAPI)
// => undefined

內容隔離表示預載指令碼與渲染器的主要世界隔離,以避免將任何具有權限的 API 洩漏到您的網路內容的程式碼中。

相反地,請使用 contextBridge 模組安全地完成此作業

preload.js
const { contextBridge } = require('electron')

contextBridge.exposeInMainWorld('myAPI', {
desktop: true
})
renderer.js
console.log(window.myAPI)
// => { desktop: true }

此功能對於兩個主要目的非常有用

  • 透過向渲染器公開 ipcRenderer 協助程式,您可以使用進程間通訊 (IPC) 從渲染器觸發主要程序工作(反之亦然)。
  • 如果您正在為託管在遠端 URL 上的現有網路應用程式開發 Electron 包裝器,您可以將自訂屬性新增至渲染器的 window 全域,這些屬性可用於網路用戶端端的僅限桌面的邏輯。

公用程式程序

每個 Electron 應用程式都可以使用 UtilityProcess API 從主要程序產生多個子程序。公用程式程序在 Node.js 環境中執行,這表示它有能力 require 模組並使用所有 Node.js API。公用程式程序可用於託管例如:不受信任的服務、CPU 密集型工作或容易當機的元件,這些元件以前會託管在主要程序或使用 Node.js child_process.fork API 產生的程序中。公用程式程序與 Node.js child_process 模組產生的程序之間的主要區別在於,公用程式程序可以使用 MessagePort 與渲染程序建立通訊通道。當需要從主要程序分支子程序時,Electron 應用程式始終可以優先使用 UtilityProcess API,而不是 Node.js child_process.fork API。

特定於程序的模組別名 (TypeScript)

Electron 的 npm 套件也會匯出包含 Electron TypeScript 類型定義子集的子路徑。

  • electron/main 包含所有主要程序模組的類型。
  • electron/renderer 包含所有渲染程序模組的類型。
  • electron/common 包含可在主要和渲染程序中執行的模組的類型。

這些別名對執行時間沒有影響,但可用於類型檢查和自動完成。

使用範例
const { app } = require('electron/main')
const { shell } = require('electron/common')