electron 应用接入 npm 包的 4 个坑

一、背景
Electron 为开发者带来很大的便利,其跨平台特性使我们可以用 JavaScript、HTML 和 CSS 来创建桌面应用。同时, Electron 应用也可以依赖 npm 包增强功能。然而,当我们在 Electron 应用中接入这些 npm 包时,可能会遇到一些坑。
本文详细介绍 Electron 应用中接入 npm 包时可能遇到的 4 个坑。希望通过这篇文章,帮读者避开这些坑,更有效地开发和优化 Electron 应用。
二、常见的 4 个坑
1、PE 文件释放到 temp 目录
electron 为了提升 require 的性能,设计出了asar。但是 dll 和 exe,或者说改后缀为 .node 的 pe 文件,却无法通过 asar 读取运行。因此,若这些文件被打包至 asar,electron 会先将文件释放至 temp 路径,然后再加载运行。

这种释放和写入文件的 I/O 操作无疑会对性能造成影响,同时,temp 路径下的 pe 文件加载也是安全软件的重点监控对象,有可能受到拦截的风险。
解决方法是在打包asar时,将.node文件设置为不打包。例如


  "build": {
    "electronVersion": "20.1.0",
    "win": {
      "target": [
        {
          "target": "nsis",
          "arch": [
            "x64",
            "ia32"
          ]
        }
      ]
    },
    "asarUnpack": [
      "node_modules/ffi-napi/**/*.node",
      "node_modules/ref-napi/**/*.node"
    ]
  },

设置后,.node 文件将在安装时直接释放至 resources\app.asar.unpacked\node_modules 目录下。

2、pdb 文件缺失
npm 包的引入有两种方式:本机编译和采用官方发布的二进制文件。但往往官方发布的二进制文件并未提供 pdb 文件,一旦 .node 文件出现 crash,问题的定位极其困难。本机编译方式也存在类似问题,许多开发者对 pdb 文件概念并不了解,或者像上述 ffi 示例,编译流程完全自动化,开发者往往忽略了保存 pdb 文件的步骤。
3、动态链接运行时库
动态与静态链接运行时库是技术上的选择,各有利弊,在桌面开发中都会被使用。动态链接运行时库后,只需保证软件打包时正确带有运行时库即可。但前端开发者通常不了解运行时库的概念,也无法识别.node文件对于运行时库的链接方式。在 .node 文件使用了动态链接运行时库并且没有带运行时库文件的情况下,就可能导致应用在部分用户机器上无法运行,尤其是某些 Windows 7 用户。
4、主线程起进程
有经验的 Windows C++ 程序员应该熟知,需要谨慎让主线程执行任务,比如避免 I/O 操作或者启动进程等,否则可能会引发主线程阻塞,导致用户界面无响应。然而,不少 npm 包在实现时直接通过启动 exe 文件来完成,如 ping 这个npm包。

让我们来看看源码,它是通过 child_process.spawn 启动 ping.exe 来实现的。

问题来了,Node.js 的 child_process.spawn 是在哪个线程调用 KERNEL32!CreateProcess 的呢?
我们设了CreateProcess 的断点,当断点被触发后,查看 0 号线程(也就是主线程),我们发现 Node.js 是在主线程执行的 CreateProcess。

CreateProcess 是一个相当耗费性能的 API,而且是安全软件的重点监控 API。若被安全软件拦截,或安全软件检查时间过长,就会造成这个 API 执行时间过长,进而导致主界面卡顿。从这个角度来看,Node.js 对 child_process 的实现方式在 Windows 桌面端场景上存在问题,而在 npm 生态中,许多 npm 包对此方法的调用,进一步放大了这个问题。
三、总结
Electron 通过 Node.js + Chromium ,为前端开发者开启了桌面程序开发的大门。这带来了开发效率的显著提升,但同时也引进了问题和挑战。
首先,我们需要承认 Node.js 和 npm 生态并非完全适应 Windows 桌面开发。Node.js 的设计初衷并不以桌面开发为主,而 npm 生态也存在类似的情况。Node.js,这个以异步编程著称的框架,实际上有一些操作在主线程运行。一旦主线程出现阻塞,就可能导致桌面界面的卡顿或无响应。很多 npm 包的实现方法,如通过执行exe来实现功能,也存在相同的问题。
其次,前端开发者往往没有桌面开发的背景,对于一些概念,如 pdb 或运行时库,并不熟悉。因此,即使他们通过导入 npm 包实现了功能,在自己的机器上运行正常,一旦发布,往往会出现兼容性问题。
通过分享文中的经验,希望能帮助到更多的 Electron 开发者,避免踩坑。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

*