Electron 中的 ES 模組 (ESM)
簡介
ECMAScript 模組 (ESM) 格式是載入 JavaScript 套件的標準方式。
Chromium 和 Node.js 都有各自的 ESM 規格實作,而 Electron 選擇使用哪個模組載入器取決於情境。
本文檔旨在概述 Electron 中 ESM 的限制,以及 Electron 中的 ESM 與 Node.js 和 Chromium 中的 ESM 之間的差異。
此功能已在 electron@28.0.0
中新增。
摘要:ESM 支援矩陣
此表格概述了 ESM 的支援位置以及使用的 ESM 載入器。
處理程序 | ESM 載入器 | 預載中的 ESM 載入器 | 適用要求 |
---|---|---|---|
主要 | Node.js | 不適用 | |
渲染器 (沙盒化) | Chromium | 不支援 | |
渲染器 (非沙盒化且內容隔離) | Chromium | Node.js | |
渲染器 (非沙盒化且非內容隔離) | Chromium | Node.js |
主要處理程序
Electron 的主要處理程序在 Node.js 情境中執行,並使用其 ESM 載入器。使用方式應遵循Node 的 ESM 文件。若要在主要處理程序中的檔案啟用 ESM,必須滿足下列條件之一
- 檔案以
.mjs
擴展名結尾 - 最近的父 package.json 已設定
"type": "module"
請參閱 Node 的判斷模組系統文件以了解更多詳細資訊。
注意事項
您必須在應用程式的 ready
事件之前大量使用 await
ES 模組是非同步載入的。這表示只有來自主要處理程序進入點匯入的副作用會在 ready
事件之前執行。
這很重要,因為某些 Electron API (例如 app.setPath
) 需要在應用程式的 ready
事件發出之前呼叫。
透過 Node.js ESM 中提供的頂層 await
,請務必 await
您需要在 ready
事件之前執行的每個 Promise。否則,您的應用程式可能會在您的程式碼執行之前就 ready
了。
對於動態 ESM 匯入語句 (靜態匯入不受影響),請特別注意這一點。例如,如果 index.mjs
在頂層呼叫 import('./set-up-paths.mjs')
,則應用程式很可能在動態匯入解析時就已經 ready
了。
// add an await call here to guarantee that path setup will finish before `ready`
import('./set-up-paths.mjs')
app.whenReady().then(() => {
console.log('This code may execute before the above import')
})
JavaScript 轉譯器 (例如 Babel、TypeScript) 在 Node.js 支援 ESM 匯入之前,就已經歷史性地支援 ES 模組語法,方法是將這些呼叫轉換為 CommonJS require
呼叫。
範例:@babel/plugin-transform-modules-commonjs
@babel/plugin-transform-modules-commonjs
外掛程式會將 ESM 匯入轉換為 require
呼叫。確切的語法將取決於 importInterop
設定。
import foo from "foo";
import { bar } from "bar";
foo;
bar;
// with "importInterop: node", compiles to ...
"use strict";
var _foo = require("foo");
var _bar = require("bar");
_foo;
_bar.bar;
這些 CommonJS 呼叫會同步載入模組程式碼。如果您要將轉譯後的 CJS 程式碼遷移到原生 ESM,請注意 CJS 和 ESM 之間的時序差異。
渲染器處理程序
Electron 的渲染器處理程序在 Chromium 情境中執行,並將使用 Chromium 的 ESM 載入器。實際上,這表示 import
語句
- 將無法存取 Node.js 內建模組
- 將無法從
node_modules
載入 npm 套件
<script type="module">
import { exists } from 'node:fs' // ❌ will not work!
</script>
如果您希望透過 npm 將 JavaScript 套件直接載入到渲染器處理程序中,我們建議使用 bundler (例如 webpack 或 Vite) 來編譯您的程式碼以供用戶端使用。
預載腳本
渲染器的預載腳本將在可用時使用 Node.js ESM 載入器。ESM 的可用性將取決於其渲染器的 sandbox
和 contextIsolation
偏好設定的值,並且由於 ESM 載入的非同步性質,還會有一些其他注意事項。
注意事項
ESM 預載腳本必須具有 .mjs
擴展名
預載腳本將忽略 "type": "module"
欄位,因此您必須在您的 ESM 預載腳本中使用 .mjs
檔案擴展名。
沙盒化預載腳本無法使用 ESM 匯入
沙盒化預載腳本以純 JavaScript 形式執行,沒有 ESM 情境。如果您需要使用外部模組,我們建議為您的預載程式碼使用 bundler。載入 electron
API 仍然透過 require('electron')
完成。
有關沙盒化的更多資訊,請參閱處理程序沙盒化文件。
非沙盒化 ESM 預載腳本將在沒有內容的頁面上於頁面載入後執行
如果渲染器載入頁面的回應主體完全空白 (即 Content-Length: 0
),則其預載腳本將不會阻止頁面載入,這可能會導致競爭條件。
如果這對您造成影響,請將您的回應主體變更為包含某些內容 (例如空的 html
標籤 (<html></html>
)),或換回使用 CommonJS 預載腳本 (.js
或 .cjs
),這將會阻止頁面載入。
ESM 預載腳本必須進行內容隔離才能使用動態 Node.js ESM 匯入
如果您的非沙盒化渲染器處理程序未啟用 contextIsolation
旗標,您就無法透過 Node 的 ESM 載入器動態 import()
檔案。
// ❌ these won't work without context isolation
const fs = await import('node:fs')
await import('./foo')
這是因為 Chromium 的動態 ESM import()
函數通常在渲染器處理程序中優先,並且在沒有內容隔離的情況下,無法知道 Node.js 是否在動態匯入語句中可用。如果您啟用內容隔離,則來自渲染器隔離預載情境的 import()
語句可以路由到 Node.js 模組載入器。