简介

Node.js 插件是一个 C++ 写的动态链接库。Node.js 中的 require() 可以直接加载插件作为一个 Node.js 模块。插件提供了 Node.js 与 C++ 库之间的接口。

实现插件有三种方式:N-API, nan 和直接用内部的 V8, libuv 以及 Node.js 库。官方推荐使用 N-API 实现插件,除非要用到 N-API 没有暴露出来的接口。

N-API

N-API 是用于构建 native Node.js 插件一系列 C API 。它独立于 JavaScript 运行时(如 V8 ),由 Node.js 团队维护。

N-API 在 Node.js 版本之间是 ABI 稳定的。意味着你只需要编译一次就可以在后续的 Node.js 版本运行。

node-addon-api

node-addon-api 将 N-API 封装成 C++ 接口,开发起来更加方便和快速。

参考文章:

从暴力到 NAN 再到 NAPI——Node.js 原生模块开发方式变迁

node-addon-api

N_API

HelloWorld

下面我们用 node-addon-api 写一个 计算 Fibonacci 插件,实现等同于下面这个 JavaScript 函数。

1
2
3
4
5
6
7
8
9
function fibonacci(n) {
    var a = 0, b = 1
    for (var i = 0; i < n; i++) {
        var tmp = a
        a = b
        b = tmp + b
    }
    return a
}

步骤

首先我们新建一个目录叫 Fibonacci ,并添加一个 package.json 文件。

1
2
3
4
5
6
7
8
{
    "name": "fibonacci",
    "version": "1.0.0",
    "devDependencies": {
        "cmake-js": "latest",
        "node-addon-api": "latest"
    }
}

node-addon-api 就是我们开发需要的 API 。

CMake.js 是一个基于 CMake 的构建 Node.js 插件的工具。所以我们需要写一个 CMakeLists.txt 。可以通过 npm 安装一个全局的 CMake.js

1
npm install -g cmake-js
cmake_minimum_required(VERSION 3.7)
project (fibonacci)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

## include
## CMake.js 会收集头文件路径到 CMAKE_JS_INC 中
include_directories(${CMAKE_JS_INC})


add_library(${PROJECT_NAME} SHARED "")

## CMake.js
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_JS_INC})

target_link_libraries(${PROJECT_NAME} PRIVATE ${CMAKE_JS_LIB})

## node-addon-api
execute_process(COMMAND node -p "require('node-addon-api').include"
  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
  OUTPUT_VARIABLE NODE_ADDON_API_DIR
  )
string(REPLACE "\n" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
string(REPLACE "\"" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})

target_sources(${PROJECT_NAME} PRIVATE
  fibonacci.cpp)

在 fibonacci.cpp 中写上我们的插件代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <napi.h>

unsigned long long Fibonacci(int n) {
    unsigned long long a = 0, b = 1;
    for (int i = 0; i < n; i++) {
        unsigned long long tmp = a;
        a = b;
        b = tmp + b;
    }
    return a;

}

Napi::Value ExportFibonacci(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();
    if (info.Length() < 1) {
        Napi::TypeError::New(env, "wrong number of arguments").ThrowAsJavaScriptException();
        return env.Null();
    }
    if (!info[0].IsNumber()) {
        Napi::TypeError::New(env, "wrong type of arguments").ThrowAsJavaScriptException();
    }
    auto n = info[0].As<Napi::Number>().Int32Value();
    auto result = Fibonacci(n);

    Napi::Number num = Napi::Number::New(env, result);
    return num;
}

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

NODE_API_MODULE(fibonacci, Init);

然后用 CMake.js 编译我们的代码,在 Fibonacci 目录执行下面的命令。

1
2
cmake-js configure
cmake-js build

最后会在 build/Release 目录下生成 fibonacci.node

现在就可以在 JavaScript 中 require 这个插件

1
2
3
const fibonacci = require("./build/Release/fibonacci.node")

console.log(fibonacci.fibonacci(10))