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

发布时间:2026/11/5 16:54:53

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/11/5 16:51:33

EasyU优启通PE维护盘实战指南:制作、启动与核心操作全解析

1. 为什么一个PE维护盘值得单独拿出来聊装系统、修引导、救数据、清病毒,这些活儿但凡干过几次的人都知道,真正决定效率的往往不是你手速多快,而是手边那套PE环境靠不靠谱。EasyU(圈内习惯叫优启通)就是在这个场景下被…

2026/11/5 16:26:13

Rust原来很简单二:所有权系统

Part 2 核心篇 —— 所有权系统 本篇覆盖 第 6 ~ 10 章:所有权、借用与引用、生命周期、字符串与切片、内存模型。 ⚠️ 这是全书最重要的一篇。 把它吃透,Rust 从此“很简单”;跳过它,你会处处碰壁。请慢下来,动手敲每一段代码。 第 6 章 所有权:Rust 的第一性原理 6…

2026/11/5 15:00:49

基于Hadoop+Spark的民宿智能推荐系统设计与实现

1. 项目概述:大数据时代的民宿智能推荐这个毕业设计项目融合了当前大数据领域最主流的技术栈,构建了一个完整的民宿推荐系统解决方案。作为一名长期从事大数据系统开发的工程师,我认为这个选题非常贴合实际应用场景——随着旅游业的复苏和共享…

2026/11/5 14:10:30

ADS122U04IPWR+STM32F427ZI工业级温度测量信号链设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 14:10:30

UART接口24位ADC搭配RS485,解决远距离温度监控难题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 14:10:30

UART接口24位ADC搭配PIC18F46K22的RTD与热电偶测温实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 0:02:07

多智能体协作系统架构设计与实践优化

1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中…

2026/11/5 0:02:07

大模型系统性入门实战指南:从原理到部署微调的全链路路线

提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#…

2026/11/5 0:02:07

鸿蒙文件系统沙箱机制解析与开发实践

1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的…

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

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

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

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