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 | N/A | |
渲染器(框) | Chromium | 不支持 | |
Renderer (Unsandboxed & Context Isolated) | Chromium | Node.js | |
Renderer (Unsandboxed & Non Context Isolated) | Chromium | Node.js |
主进程
Electron's main process runs in a Node.js context and uses its ESM loader. Usage should follow Node's ESM documentation. To enable ESM in a file in the main process, one of the following conditions must be met:
- The file ends with the
.mjs
extension - The nearest parent package.json has
"type": "module"
set
See Node's Determining Module System doc for more details.
注意事项
You must use await
generously before the app's ready
event
ES Modules are loaded asynchronously. This means that only side effects
from the main process entry point's imports will execute before the ready
event.
这很重要,因为某些Electron API(例如[app.setPath](latest/api/app.md#appsetpathname-path)) 需要调用 **之前** 应用程序的
ready` 事件。
With top-level await
available in Node.js ESM, make sure to await
every Promise that you need to
execute before the ready
event. 否则,您的应用可能会在代码执行之前`ready'。
This is particularly important to keep in mind for dynamic ESM import statements (static imports are unaffected).
For example, if index.mjs
calls import('./set-up-paths.mjs')
at the top level, the app will
likely already be ready
by the time that dynamic import resolves.
// 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) 历来支持节点前的 ES模块
语法。 s 支持ESM导入,将这些通话转到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 加载器。
In practice, this means that import
statements:
- will not have access to Node.js built-in modules
- will not be able to load npm packages from
node_modules
<script type="module">
import { exists } from 'node:fs' // ❌ will not work!
</script>
If you wish to load JavaScript packages via npm directly into the renderer process, we recommend using a bundler such as webpack or Vite to compile your code for client-side consumption.
Preload 脚本
A renderer's preload script will use the Node.js ESM loader when available.
ESM availability will depend on the values of its renderer's sandbox
and contextIsolation
preferences, and comes with a few other caveats due to the asynchronous nature of ESM loading.
注意事项
ESM preload scripts must have the .mjs
extension
Preload scripts will ignore "type": "module"
fields, so you must use the .mjs
file
extension in your ESM preload scripts.
Sandboxed preload scripts can't use ESM imports
Sandbox预加载脚本作为普通JavaScript运行,没有一个 ESM 环境。 如果您需要
使用外部模块,我们建议使用捆绑程序进行预加载代码。 加载
electronel
API 仍然通过 require('electrony)
完成。
For more information on sandboxing, see the Process Sandboxing docs.
卸载的 ESM 预加载脚本将在没有内容的页面加载后运行
如果渲染器加载页面的响应正文为 completely 空(例如) Content-Leng:0
,
其预加载脚本不会阻止页面加载,这可能导致种族条件。
If this impacts you, change your response body to have something in it
(e.g. an empty html
tag (<html></html>
)) or swap back to using a CommonJS preload script
(.js
or .cjs
), which will block the page load.
ESM 预加载脚本必须是孤立的上下文才能使用动态Node.js ESM 导入
If your unsandboxed renderer process does not have the contextIsolation
flag enabled,
you cannot dynamically import()
files via Node's ESM loader.
// ❌ these won't work without context isolation
const fs = await import('node:fs')
await import('./foo')
This is because Chromium's dynamic ESM import()
function usually takes precedence in the
renderer process and without context isolation, there is no way of knowing if Node.js is available
in a dynamic import statement. 如果您启用上下文隔离,则可以将渲染器的孤立预载入上下文中的导入()
语句
路由到 Node.js 模块加载器。