如何为Electron应用开发原生模块

大家都知道JAVAScript无论是在浏览器中运行、还是在Node.js中运行都是单线程运行的,所以并不适合在处理一些CPU密集型任务 。但是Node.js允许开发者使用C、C++等语言开发像普通的Node.js模块一样通过require()函数加载的原生模块 。因为Electron内置Node.js,这样就使得Electron同样具备了相同的能力 。
在实际业务场景中,也有一些现成的C/C++项目,在Node.js项目中直接复用可以节约很多开发成本 。
本文将探讨如何在Electron应用中开发原生模块,以扩展应用的功能和性能 。
搭建原生模块开发环境在目前的原生模块开发中,一般都是基于Node-Api进行开发 。
Node-Api是什么呢?Node-API(Node Application Programming Interface)是一个用于编写跨平台原生插件的封装层 。它提供了一组稳定的 C/C++ 函数,使开发者可以编写与 Node.js 运行时环境兼容的原生插件 。通过使用 Node-API,开发者可以消除由于 Node.js 版本变化而引起的插件不兼容的问题,并且能够更方便地编写和维护跨平台的原生模块 。
理解一下就是我们无需为不同版本的Node.js编译不同版本的原生模块 。不同版本的 Node.js 使用同样的接口为原生模块提供服务,这些接口是 ABI 化的 , 只要 ABI 的版本号一致,编译好的原生模块就可以直接使用,而不需要重新编译 。
【如何为Electron应用开发原生模块】ABI 化是指将软件接口转化为应用程序二进制接口(Application Binary Interface)的过程 。在编程中,ABI 化的目标是确保在不同编译器、操作系统以及硬件平台之间的二进制兼容性 。通过将接口规范化为二进制标准 , 不同模块或程序可以相互调用和交互,而无需关心具体实现细节和底层平台差异 。Node-API 的设计就是为了实现跨版本和跨平台的 ABI 兼容性,以便 C/C++ 模块能够在不同的 Node.js 环境中无需重新编译即可运行 。
Node-API有哪些开发方式?基于 Node-API 的原生模块开发可以使用 C 语言或者基于 node-addon-api 项目使用 C++ 语言的两种方式 。

  1. 基于C语言开发:由于受众为前端开发者,C语言的编程复杂度高、开发效率较低,开发过程可能较为繁琐 。
  2. 基于 node-addon-api项目开发:相对于纯 C 语言开发 , C++ 提供了更多的高级特性和工具,开发效率相对较高 。node-addon-api 项目提供了一组方便的 C++ API 封装 , 简化了与 Node-API 的交互过程,减少了部分底层操作 。
接下来我们就基于这个项目来开发一个 Electron 的原生模块 。
  1. 安装 Node.js:首先,确保您已经安装了 Node.js,可以从 Node.js 官网下载并安装适合您操作系统的版本 。
  2. 需要全局安装 node-gyp,它是专门为构建开发、编译原生模块环境而生的跨平台命令行工具 。
npm install -g node-gyp
  1. 创建空项目目录:创建一个新的项目目录,作为原生模块的开发目录 。
  2. 初始化 npm 项目:进入项目目录,打开终端 , 并运行以下命令初始化 npm 项目 。
 npm init
  1. 根据提示 , 设置项目的名称、版本等信息 。
  2. 安装 node-addon-api:运行以下命令安装 node-addon-api 模块:
npm install node-addon-api --save-dev以上就已经搭建好了基本的原生模块开发环境,接下来通过一个简单的例子,来实现一个访问系统文件的原生模块带大家了解一下开发流程 。
开发原生模块访问系统文件并读取文件内容开发原生模块需要熟悉C++编程和Node.js的C/C++扩展开发,涉及一些底层的编程和跨平台的知识 。这里的例子较为简单,方便大家理解 。
  1. 创建 C++ 文件:在项目目录下创建一个 C++ 源文件 , 例如 filesystem.cpp,并添加以下代码内容:
#include <napi.h>#include <IOStream>#include <fstream> Napi::String ReadFile(const Napi::CallbackInfo& info) {Napi::Env env = info.Env();// 读取文件路径参数std::string filePath = info[0].As<Napi::String>().Utf8Value();// 打开文件并读取内容 std::ifstream file(filePath);std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());file.close();// 将内容转换为 Napi::String 返回return Napi::String::New(env, content); } Napi::Object Init(Napi::Env env, Napi::Object exports) {exports.Set("readFile", Napi::Function::New(env, ReadFile));return exports; }NODE_API_MODULE(addon, Init)


推荐阅读