Electron 如何在主进程与 preload 脚本中启用 ES Modules?

发布时间:2026/9/11 17:58:09

Electron 如何在主进程与 preload 脚本中启用 ES Modules? Electron 如何在主进程与 preload 脚本中启用 ES Modules【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron如果你的 Electron 应用想用import语句加载模块而不是 CommonJS 的require需要满足两个前提项目使用的 Electron 版本不低于 28.0.0ESM 支持在electron28.0.0加入并且清楚不同进程使用哪套模块加载器。Chromium 和 Node.js 各自有一套 ESM 实现Electron 会按上下文选择主进程走 Node.js 的 ESM loader渲染器页面走 Chromium 的 ESM loaderpreload 脚本则在可用时走 Node.js 的 ESM loader。本文按“搭好项目 → 主进程启用 ESM → preload 启用 ESM → 验证”的顺序把这条路径完整走一遍所有行为细节以 ES Modules (ESM) in Electron 为准。ESM 支持矩阵先确认你的脚本运行在哪个上下文官方给出的支持矩阵如下摘自 esm.mdProcessESM LoaderESM Loader in PreloadApplicable RequirementsMainNode.jsN/Aready事件前必须充分使用awaitRenderer (Sandboxed)ChromiumUnsupported沙箱 preload 不能使用 ESM importRenderer (Unsandboxed Context Isolated)ChromiumNode.jsESM preload 必须用.mjs扩展名空内容页面上 preload 在页面加载后才运行Renderer (Unsandboxed Non Context Isolated)ChromiumNode.js同上由此可以直接得出两条操作结论主进程启用 ESM 只需要处理文件扩展名和package.jsonpreload 启用 ESM 则必须先解除沙箱再处理扩展名和contextIsolation相关的限制。搭建可运行的 Electron 项目先按 Building your First App 的流程初始化项目这一步与是否使用 ESM 无关但入口文件随后会指向 ESM 脚本mkdir my-electron-app cd my-electron-app npm init npm install electron --save-devnpm init时把 entry point 指向你的 ESM 入口文件例如index.mjs。Electron 可执行文件应放在devDependencies中并通过scripts里的electron .命令以开发模式运行{ name: my-electron-app, main: index.mjs, type: module, scripts: { start: electron . }, devDependencies: { electron: ^28.0.0 } }上面是组合示例main指向入口、scripts: { start: electron . }来自官方教程的模板type: module则来自仓库测试夹具 spec/fixtures/esm/package/package.json。注意type: module只对主进程这类 Node.js 上下文生效对 preload 脚本无效见下文。另外教程特别提醒Electron 打包工具链要求node_modules真实落在磁盘上如果你使用 Yarn Berry 或 pnpm需要分别设置nodeLinker: node-modules或nodeLinker: hoisted否则安装策略不兼容。主进程启用 ESM两种满足其一的启用条件主进程运行在 Node.js 上下文中要让某个文件按 ESM 处理以下条件满足其一即可esm.md文件以.mjs结尾最近的父级package.json中设置了type: module。仓库测试夹具提供了两种形态的最小主进程入口可以直接作为模板。第一种是.mjs入口spec/fixtures/esm/entrypoint.mjs仓库示例import * as electron from electron; console.log(ESM Launch, ready:, electron.app.isReady()); process.exit(0);第二种是依赖type: module的包入口package.json只多一个main: index.mjsspec/fixtures/esm/package/index.mjsimport * as electron from electron; console.log(ESM Package Launch, ready:, electron.app.isReady()); process.exit(0);运行npm run start后如果入口加载成功终端会打印对应的一行日志ready: false表示打印时ready事件尚未触发这正是 ESM 异步加载的体现。ready事件前必须充分使用awaitESM 是异步加载的ready事件之前只会执行主进程入口自身 import 的副作用。而某些 API例如app.setPath必须在ready事件之前调用因此需要利用 Node.js ESM 的 top-levelawait把每一个必须在ready前完成的 Promise 都await掉。仓库夹具 spec/fixtures/esm/top-level-await.mjs 演示了这一点import * as electron from electron; // Cheeky delay await new Promise((resolve) setTimeout(resolve, 500)); console.log(Top level await, ready:, electron.app.isReady()); process.exit(0);仓库测试断言在 top-levelawait期间electron.app.isReady()仍为false即 Electron 会等待 top-levelawait完成才宣告 app ready。这一点在动态import()时尤其危险。静态 import 不受影响但如果在顶层调用动态 import 而不await等它 resolve 时 app 很可能已经ready了。esm.md 中的原始示例文档给出的就是“缺少 await”的错误写法注释指明了修复位置// 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) })修复方式就是把那行动态 import 改为await import(./set-up-paths.mjs)保证路径设置在ready之前完成。从转译后的 CJS 代码迁移时的时间差异Babel、TypeScript 等转译器历史上会把import语法转成 CommonJS 的require调用例如babel/plugin-transform-modules-commonjs插件具体产物取决于importInterop配置。require是同步加载模块代码的如果你把转译成 CJS 的代码迁移到原生 ESM要注意两者加载时序的差异否则依赖“模块加载即执行完毕”的逻辑可能出现时序问题。preload 脚本启用 ESMpreload 脚本要使用 Node.js 的 ESM loader条件比主进程多按顺序处理以下四步。1. 解除沙箱从 Electron 20 开始preload 脚本默认是沙箱化的沙箱 preload 以纯 JavaScript 运行没有 ESM 上下文不能写 ESM import。如果需要拆分模块官方建议用 bundler如 webpack打包 preload 代码此时electronAPI 仍通过require(electron)加载Process Sandboxing。要在 preload 里用 ESM必须把渲染器进程设为非沙箱在BrowserWindow构造参数的webPreferences中设置sandbox: false。注意解除沙箱带有安全风险尤其当进程中存在不受信任的代码或内容时文档明确提醒这一点。2. 文件必须使用.mjs扩展名preload 脚本会忽略package.json中的type: module字段所以 ESM preload 必须用.mjs文件扩展名即使主进程是靠type: module启用 ESM 的。3. 动态import()需要 context isolationpreload 里的静态import语句没有额外限制但如果是通过 Node 的 ESM loader 做动态import()则要求渲染器进程启用了contextIsolation该选项自 Electron 12 起默认开启Context Isolation// ❌ these wont work without context isolation const fs await import(node:fs) await import(./foo)原因是渲染器进程中 Chromium 的动态import()通常优先生效没有 context isolation 时无法判断动态 import 语句里 Node.js 是否可用启用 context isolation 后来自 preload 隔离上下文的import()才能路由到 Node.js 模块加载器。4. 空内容页面的竞态问题如果渲染器加载的页面响应体完全为空Content-Length: 0非沙箱的 ESM preload 不会阻塞页面加载可能导致竞态条件。两种解法均出自 esm.md让响应体里有一点内容例如html/html或者换回 CommonJS preload.js或.cjs它会阻塞页面加载。仓库中的完整 ESM preload 示例仓库测试夹具 spec/fixtures/esm/import-meta/ 给出了一个可对照的主进程 preload 组合。主进程入口main.mjs 简化自仓库示例去掉了测试用的断言与退出逻辑import { app, BrowserWindow } from electron; import { fileURLToPath } from node:url; async function createWindow() { const mainWindow new BrowserWindow({ show: false, webPreferences: { preload: fileURLToPath(new URL(preload.mjs, import.meta.url)), sandbox: false, contextIsolation: false } }); await mainWindow.loadFile(index.html); } app.whenReady().then(() createWindow());对应的 preload.mjs仓库示例展示import.meta在 ESM preload 中可用import { fileURLToPath } from node:url; window.importMetaPath fileURLToPath(import.meta.url);两点说明其一该夹具里 preload 路径用fileURLToPath(new URL(preload.mjs, import.meta.url))计算即 ESM 主进程里定位自身目录的写法其二夹具显式设置了contextIsolation: false因此 preload 里对window的赋值能被页面直接读到——如果保持 context isolation 开启preload 与页面不在同一个window上下文暴露 API 应改走contextBridgeContext Isolation。若你的 preload 需要上文第 3 条的动态import()则必须启用 context isolation。验证 ESM 是否真正生效仓库测试套件 spec/esm-spec.ts 展示了两种可复用的验证方式。主进程入口以 ESM 入口启动应用检查退出码和标准输出。仓库测试对 entrypoint.mjs 的期望是退出码为 0、stdout 恰好为ESM Launch, ready: false测试断言值即文档示例级别的预期输出。对你自己的应用npm run start后终端打印出你写在入口里的日志即说明 ESM 入口被正确加载。preload仓库测试为每个测试窗口挂上preload-error事件监听 preload 加载错误再用webContents.executeJavaScript读取 preload 暴露到页面的全局判断其类型是否符合预期let error null; w.webContents.on(preload-error, (_, __, err) { error err; }); await w.loadFile(index.html); // preload 中执行了 import { resolve } from path; window.resolvePath resolve; const exposedType await w.webContents.executeJavaScript(typeof window.resolvePath); expect(exposedType).to.equal(function);以上取自 spec/esm-spec.ts 的测试代码作为示例展示。在真实应用中等价做法是监听preload-error若无错误且页面脚本能读到 preload 挂上去的对象/函数说明 ESM preload 已被加载并执行。仓库测试还覆盖了几个值得知道的边界import electron/main、import electron/renderer、import electron/common、import electron/utility都可以正常导入而类似import electron/lol这样的不存在入口会抛ERR_MODULE_NOT_FOUND主进程或Cannot find package electronpreloadESM preload 的导入链完成前页面加载会被推迟preload 里的 top-levelawait会阻塞页面加载。限制与边界版本门槛ESM 支持自electron28.0.0起提供低于该版本的主进程入口和 ESM preload 均不可用。渲染器页面本身页面里的import走 Chromium 的 ESM loader既不能访问 Node.js 内置模块也不能从node_modules加载 npm 包import { exists } from node:fs在页面中无效。需要给渲染器引入 npm 包时官方建议使用 webpack、Vite 等 bundler 编译成客户端可消费的代码。沙箱 preload永远无法使用 ESM import只能靠 bundler 拆分子模块electronAPI 继续用require(electron)加载。preload 的模块判定只看扩展名type: module对 preload 无效.mjs是唯一开关。动态import()preload 中未经 context isolation 的动态import()无法走 Node.js 的 ESM loader。空页面响应体为空的页面上非沙箱 ESM preload 不阻塞页面加载需要按上文方法规避竞态。完成以上配置后你的项目应同时具备npm run start能加载.mjs或type: module下的主进程入口并打印日志以及一个.mjs命名的非沙箱 preload 成功执行、且preload-error事件没有触发。若 preload 报错优先按本文顺序核对渲染器是否sandbox: false、扩展名是否为.mjs、是否涉及未启用 context isolation 的动态import()、加载的页面响应体是否为空。更多行为细节可回查 ESM 指南 与仓库测试 spec/esm-spec.ts。【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/11 23:24:13

人形机器人如何倒逼MCU走向集成极限

1. 为什么人形机器人正在把MCU逼上“集成极限”最近在几家头部人形机器人公司的产线蹲点时,我亲眼看到一个现象:三年前还在用三颗独立MCU分别管电机驱动、IMU姿态解算和电池管理的控制板,现在被一块指甲盖大小的芯片全包了。不是FPGA&#xf…

2026/9/11 23:24:13

RDK X5开发板MIPI、SPI、I2C接口区别与调试实战指南

RDK X5 的 MIPI、SPI、I2C 接口,到底有啥区别?这个问题我当初刚拿到开发板的时候也纠结了很久。尤其是一看原理图,MIPI 那边几十个引脚密密麻麻,SPI 和 I2C 都只有四五根线,但摄像头、屏幕、传感器、Flash、电机驱动全…

2026/9/11 23:24:13

大功率终端负载选型指南:N型与7-16接口实战对比

1. 选型背景:为什么大功率终端负载会成为一个“项目”做射频的人基本都经历过这种场景:功放调试完要装机,总得先找个地方把输出功率“吃掉”;或者天线馈源拆下来检修,发射机不能干烧,得用负载顶着&#xff…

2026/9/11 23:24:13

如何用 Docker 镜像 ghcr.io/astral-sh/ruff 在容器内执行 ruff check

如何用 Docker 镜像 ghcr.io/astral-sh/ruff 在容器内执行 ruff check 【免费下载链接】ruff An extremely fast Python linter and code formatter, written in Rust. 项目地址: https://gitcode.com/GitHub_Trending/ru/ruff 当你想在容器环境里对 Python 代码做 lint…

2026/9/11 23:19:13

Java财务管理系统:JSP+Servlet企业级毕设实战

简介:本资源是一套完整的Java毕业设计项目——企业财务管理系统,面向计算机类本科生及Java初学者,解决毕业设计选题、系统开发、论文撰写与答辩全流程需求。压缩包共14个文件,包含3个MP4项目讲解视频(覆盖环境部署、部…

2026/9/10 16:39:38

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/10 11:16:38

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/10 12:32:02

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/10 15:19:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/10 15:49:53

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码