程序模型
Electron 從 Chromium 繼承了其多程序架構,這使得此框架在架構上非常類似於現代化的網頁瀏覽器。本指南將擴展教學中應用的概念。
為何不使用單一程序?
網頁瀏覽器是非常複雜的應用程式。除了顯示網頁內容的主要功能外,它們還有許多次要職責,例如管理多個視窗(或分頁)和載入第三方擴充功能。
在早期,瀏覽器通常使用單一程序來處理所有這些功能。雖然這種模式意味著每個開啟的分頁的額外負擔較少,但也意味著一個網站崩潰或停止回應會影響整個瀏覽器。
多程序模型
為了解決這個問題,Chrome 團隊決定每個分頁都會在其自己的程序中渲染,從而限制了網頁上錯誤或惡意程式碼可能對整個應用程式造成的損害。然後,單一瀏覽器程序會控制這些程序,以及整個應用程式生命週期。以下來自Chrome Comic的圖表將此模型視覺化
Electron 應用程式的結構非常相似。作為應用程式開發人員,您可以控制兩種程序類型:主程序和渲染程序。這些程序類似於 Chrome 自己的瀏覽器和渲染程序,如上所述。
主程序
每個 Electron 應用程式都有一個單一主程序,它充當應用程式的進入點。主程序在 Node.js 環境中執行,這表示它能夠 require
模組並使用所有 Node.js API。
視窗管理
主程序的主要目的是使用 BrowserWindow
模組建立和管理應用程式視窗。
BrowserWindow
類別的每個實例都會建立一個應用程式視窗,該視窗會在單獨的渲染程序中載入網頁。您可以使用視窗的 webContents
物件,從主程序與此網頁內容互動。
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
模組控制應用程式的生命週期。此模組提供大量事件和方法,您可以使用它們來新增自訂應用程式行為(例如,以程式設計方式結束應用程式、修改應用程式 Dock 或顯示「關於」面板)。
作為一個實際範例,教學入門程式碼中顯示的應用程式使用 app
API 來建立更原生的應用程式視窗體驗。
// 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 檔案是渲染程序的進入點。
- UI 樣式是透過階層式樣式表 (CSS) 新增的。
- 可執行的 JavaScript 程式碼可以透過
<script>
元素新增。
此外,這也意味著渲染器無法直接存取 require
或其他 Node.js API。為了直接在渲染器中包含 NPM 模組,您必須使用與在網頁上使用的相同捆綁工具鏈(例如,webpack
或 parcel
)。
為了方便開發,渲染程序可以使用完整的 Node.js 環境產生。從歷史上看,這曾經是預設值,但出於安全考量,此功能已被停用。
此時,您可能想知道,如果這些功能只能從主程序存取,您的渲染程序使用者介面如何與 Node.js 和 Electron 的原生桌面功能互動。事實上,沒有直接匯入 Electron 內容腳本的方法。
預載腳本
預載腳本包含在網頁內容開始載入之前在渲染程序中執行的程式碼。這些腳本在渲染器上下文中執行,但透過存取 Node.js API 而被授予更多權限。
預載腳本可以附加到 BrowserWindow
建構子的 webPreferences
選項中的主程序。
const { BrowserWindow } = require('electron')
// ...
const win = new BrowserWindow({
webPreferences: {
preload: 'path/to/preload.js'
}
})
// ...
由於預載腳本與渲染器共享全域 Window
介面,並且可以存取 Node.js API,因此它可以透過在 window
全域中公開任意 API 來增強您的渲染器,然後您的網頁內容可以使用這些 API。
雖然預載腳本與它們附加到的渲染器共享 window
全域,但由於 contextIsolation
預設值,您無法直接將任何變數從預載腳本附加到 window
。
window.myAPI = {
desktop: true
}
console.log(window.myAPI)
// => undefined
Context Isolation 表示預載腳本與渲染器的主要世界隔離,以避免將任何特權 API 洩漏到您的網頁內容程式碼中。
相反地,請使用 contextBridge
模組來安全地完成此操作
const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('myAPI', {
desktop: true
})
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
與渲染程序建立通訊通道。當需要從主程序 fork 子程序時,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')