Electron 加载原生模块总崩溃?搞懂这两行配置就够了

Electron 加载原生模块总崩溃?搞懂这两行配置就够了

用 Electron + Vite 做桌面端开发,肯定会遇到这个情况:引入 C++ 原生模块。

比如你想搞个全局快捷键、监听鼠标宏,引入了 uiohook-napi 或者 koffi。 结果往往是:本地开发一启动就白屏报错,或者好不容易本地跑通了,打包成 exe 发给测试,双击秒退。 然后,弹出原生模块/依赖确实的弹窗。

如果你也经历过这种问题,大概率是没搞定这两部分:Vite 的打包拦截 和 Electron Builder 的 ASAR 归档。

今天我们就扒一扒 vite.config.ts 和 electron-builder.js 里,那两段看起来毫不起眼、却救了无数命的配置。

先搞清楚:原生模块为什么这么"娇贵"?

平时我们写的 JS/TS,Vite 随便揉捏,打包成什么样子都能跑。但像 uiohook-napi 这种底层库,它们最终会编译成一个 .node 后缀的二进制文件(动态链接库)。

这玩意儿有两个注意点:

  1. Vite 不认识它:强行用 Esbuild/Rollup 去打包分析二进制文件,路径绝对错乱。
  2. 操作系统的底层加载机制很笨:它只能通过绝对路径去硬盘上找真实的物理文件,读不懂虚拟的压缩包。

针对这两点,我们需要在不同的阶段下药。


第一道防线:保本地开发

位置 :vite.config.ts 的 plugins 里

typescript 复制代码
plugins: [
  isServe && notBundle({
    filter: ['electron-log', 'uiohook-napi']
  })
]

场景还原:

当你敲下 npm run dev,Vite 开始接管主进程的编译。它的本能是把 node_modules 里的依赖全打包到一起。一旦它碰到 uiohook-napi,由于里面掺杂了 .node 文件的引用,打包过程就会出岔子,导致主进程直接挂掉。

这行代码干了啥?

notBundle 插件的作用很简单,就是"按兵不动"。配置了 filter: ['uiohook-napi'] 后,相当于告诉 Vite:"碰到这个库,你别管,别打包,把 import 原样转成 require() 留着。"

这样,代码运行时实际上是 Node.js 运行时直接去 node_modules 目录里动态加载真实的二进制文件,本地开发就顺滑了。

顺便提个坑 : 你可能会在网上看到另一种函数写法:filter: (id) => ['uiohook-napi'].includes(id) ? false : undefined。

千万别这么写! 在这个插件里,返回 false 等于"放行让 Vite 打包",返回 undefined 才是"不打包"。上面那种函数写法相当于把 notBundle 给废了,原生模块照样崩溃。直接用数组 ['xxx'] 才是最稳的标准姿势。


第二道防线:保生产打包

位置 :electron-builder.js

javascript 复制代码
asarUnpack: [
  "**/*.node"
]

场景还原:

本地 dev 好不容易没报错了,你信心满满地执行 npm run build,然后拿着生成的安装包去别人电脑上安装。双击图标,转圈,闪退。翻开日志一看:Error: dlopen failed...。

这行代码又干了啥?

electron-builder 打包时,默认会把你的代码、资源全塞进一个叫 app.asar 的归档文件里(你可以理解为一种没有后缀的 zip 虚拟目录)。

但是!操作系统底层加载 .node 文件用的 dlopen 方法,它是个"老实人",它不认 asar 这种虚拟文件系统,它必须去硬盘的真实路径上找文件。找不到,就直接抛异常闪退。

加上 asarUnpack: ["**/*.node"] 后,打包工具在生成 app.asar 的同时,会像个扫地僧一样,把所有 .node 文件单独挑出来,放进一个叫 app.asar.unpacked 的真实物理文件夹里。

当用户打开你的软件,Electron 底层做了个聪明的兼容:发现你要加载原生模块,它会自动去 unpacked 目录里找。这样,生产环境也稳了。


总结:一个原生模块的"通关"历程

graph TD Start[.node原生模块] --> Vite subgraph 第一关 Vite本地编译 Vite{Vite默认打包} Vite -->|未拦截| Fail1[路径错乱 启动报错] Vite -->|notBundle拦截| Keep[保留require原样] Keep --> Load[动态加载node_modules] Load --> DevOK[开发环境通过] end DevOK --> Asar subgraph 第二关 Builder生产打包 Asar{默认压入asar} Asar -->|未拦截| Fail2[无法读取 线上闪退] Asar -->|asarUnpack拦截| Unpack[解压至unpacked目录] Unpack --> BuildOK[生产环境通过] end

如上图,我们可以把这两行配置看作是一个原生模块在项目里的两个关卡:

  1. 开发期(Vite) :notBundle 把它拦下,不让进打包队列了,直接走 require cjs通道去 node_modules 找本体吧。"
  2. 打包期(ASAR) :asarUnpack 把它拦下,不压进虚拟压缩包,去旁边的 unpacked 目录待着。"

两道关卡各司其职,少一个,你的 Electron 应用在某个阶段必定崩给你看。下次再遇到原生模块加载失败,直接查这两个地方,基本应该没啥问题。

相关推荐
架构技术专栏6 小时前
Agent 工具调用:从工具声明到执行反馈
前端·人工智能·后端
别动我齐刘海7 小时前
C++ 工程化学习
java·linux·前端·c++·学习·机器学习·机器人
志尊宝8 小时前
Vue3 零基础每日笔记(071):暗黑模式与主题切换——CSS 变量 + Element Plus 暗黑方案
前端·css·笔记·vue·html·前端开发·软件开发
刘发财8 小时前
前端如何生成10000页PDF文件?
前端·javascript·开源
A接拉起0079 小时前
2026 年 JavaScript 开发者需要了解的知识
前端·javascript·后端
用户11481867894849 小时前
面试官:大屏项目里十几个模块同时渲染上万条数据,怎么做到不卡?
前端·性能优化
前端市界9 小时前
Pro React Admin: 统一工程底座,承载多种应用...
前端·github
葡萄城技术团队9 小时前
dsh-spreadjs-editor 当前支持什么:安装前需要了解的几件事
前端
三*一9 小时前
优化修整算法
前端·javascript·算法
weifont11 小时前
自动化抓取和上架商品:能不动手,就少动手
运维·前端·自动化