使用C++和N-API编写NodeJS插件的初学者指南

作者:API传播员 · 2025-12-17 · 阅读时间:6分钟
本指南介绍如何使用C++和N-API编写Node.js插件,涵盖从项目初始化、依赖安装到函数和类导出的完整流程,帮助初学者快速上手N-API插件开发,提升Node.js应用性能。

使用 C++ 和 N-API 编写 NodeJS 插件的初学者指南

在 Node.js 开发中,编写插件的需求可能源于以下几个原因:

  1. 需要访问一些使用 JavaScript 难以操作的原生 API。
  2. 希望集成用 C/C++ 编写的第三方库,并直接在 Node.js 中调用。
  3. 出于性能优化的考虑,可能需要将部分模块用 C++ 重写。

无论你的理由是什么,这篇文章将重点介绍如何使用 N-API 构建基于 C/C++ 的 Node.js 插件。


什么是 N-API?

N-API 是一个稳定的 API,用于通过 C 或 C++ 构建 Node.js 插件。从 Node.js v10 开始,N-API 已经成为稳定功能(在 v8 和 v9 中为实验性功能)。使用 N-API 构建的插件可以在不同版本的 Node.js 中保持兼容性。

为了简化开发,我们将使用 node-addon-api 包。它是 N-API 的 C++ 包装器,提供了低开销的 C++ 对象模型和异常处理语义。


创建基本的 Node.js 项目并测试插件

初始化项目

首先,创建一个新的 Node.js 项目并初始化:

mkdir test-addon
cd test-addon
git init
npm init

安装依赖

安装必要的依赖项:

npm install node-gyp --save-dev
npm install node-addon-api
  • node-gyp 是用于编译插件的工具链。
  • node-addon-api 是一个辅助库,简化了 C++ 插件的开发。

配置项目文件

package.json 文件中,添加以下属性:

"gypfile": true

然后创建 binding.gyp 文件,用于定义编译配置:

{
  "targets": [
    {
      "target_name": "testaddon",
      "sources": ["cppsrc/main.cpp"]
    }
  ]
}

接着,在 package.json 中指定主文件为 index.js


编译并运行插件

构建插件

运行以下命令编译插件:

npm run build

如果一切顺利,你应该会看到类似以下的输出:

> test-addon@1.0.0 build
> node-gyp rebuild

测试插件

创建 index.js 文件,并添加以下内容:

const addon = require('./build/Release/testaddon');
console.log(addon);

运行 index.js,你应该看到插件的输出。


导出 C++ 函数到 Node.js

示例:导出简单函数

创建一个新的 C++ 文件 cppsrc/Samples/functionexample.cpp,内容如下:

#include 

Napi::String HelloWrapped(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();
    return Napi::String::New(env, "Hello World");
}Napi::Object Init(Napi::Env env, Napi::Object exports) {
    exports.Set(Napi::String::New(env, "hello"), Napi::Function::New(env, HelloWrapped));
    return exports;
}NODE_API_MODULE(addon, Init)

修改 binding.gyp 文件,将新文件添加到 sources 中:

"sources": ["cppsrc/main.cpp", "cppsrc/Samples/functionexample.cpp"]

重新运行构建命令:

npm run build

更新 index.js 文件以测试新功能:

const addon = require('./build/Release/testaddon');
console.log(addon.hello());

运行 index.js,你应该看到以下输出:

Hello World

导出带参数的函数

示例:实现加法函数

cppsrc/Samples/functionexample.cpp 中,添加以下代码:

Napi::Number AddWrapped(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();
    double arg0 = info[0].As().DoubleValue();
    double arg1 = info[1].As().DoubleValue();
    return Napi::Number::New(env, arg0 + arg1);
}

Napi::Object Init(Napi::Env env, Napi::Object exports) {
    exports.Set(Napi::String::New(env, "add"), Napi::Function::New(env, AddWrapped));
    return exports;
}

重新构建并运行以下代码:

const addon = require('./build/Release/testaddon');
console.log(addon.add(5, 10));

输出结果应为:

15

导出 C++ 类到 Node.js

示例:创建一个简单类

创建 cppsrc/Samples/classexample.hcppsrc/Samples/classexample.cpp 文件,定义一个简单的 C++ 类,并将其导出到 Node.js。

classexample.h

#include 

class ClassExample : public Napi::ObjectWrap {
public:
    static Napi::Object Init(Napi::Env env, Napi::Object exports);
    ClassExample(const Napi::CallbackInfo& info);private:
    double value_;
    Napi::Value GetValue(const Napi::CallbackInfo& info);
    void SetValue(const Napi::CallbackInfo& info, const Napi::Value& value);
};

classexample.cpp

#include "classexample.h"

Napi::Object ClassExample::Init(Napi::Env env, Napi::Object exports) {
    Napi::Function func = DefineClass(env, "ClassExample", {
        InstanceMethod("getValue", &ClassExample::GetValue),
        InstanceMethod("setValue", &ClassExample::SetValue)
    });    exports.Set("ClassExample", func);
    return exports;
}ClassExample::ClassExample(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) {
    this->value_ = info[0].As().DoubleValue();
}Napi::Value ClassExample::GetValue(const Napi::CallbackInfo& info) {
    return Napi::Number::New(info.Env(), this->value_);
}void ClassExample::SetValue(const Napi::CallbackInfo& info, const Napi::Value& value) {
    this->value_ = value.As().DoubleValue();
}

binding.gyp 中添加新文件,并重新构建项目。

测试代码:

const addon = require('./build/Release/testaddon');
const instance = new addon.ClassExample(4.3);
console.log(instance.getValue());
instance.setValue(7.6);
console.log(instance.getValue());

输出结果:

4.3
7.6

总结

通过本文的介绍,我们学习了如何使用 N-API 和 C++ 开发 Node.js 插件。从简单的函数导出到复杂类的实现,N-API 提供了强大的功能和稳定的接口。如果你想深入了解,可以参考 完整代码示例。希望本文能帮助你更好地理解和使用 N-API! 🚀

原文链接: https://blog.atulr.com/node-addon-guide/