以前为 Node.js 编写拓展的时候,使用的是 Node.js 的 C++ addon API,直接使用 v8 提供的 api 和 Node.js 打交道。使用 C++ Addon 的 API 有个缺点就是 ABI 层面不兼容,升级 Node.js 之后 extension 要重新编译。
现在 Node.js 提供了 N-API。这个 API 是 C 层面的支持(也有 C++ 绑定)。同时,使用 N-API 写出来的拓展是 ABI 兼容的。
而使用 N-API 编写 Node.js 扩展不一定用 node-gyp 进行编译,也可以使用 cmake, 这对一些使用 CMake 的 C++ 项目可以说非常友好了。
使用方法
按照官方教程:
$ yarn add node-addon-api cmake-js bindings
然后在 package.json
里面加上一句:
"scripts": {
"install": "cmake-js compile"
}
然后愉快的 yarn install
就可以使用了。
贴一下我的 CMakeList.txt 配置:
project (zparser)
cmake_minimum_required(VERSION 3.15)
set(CMAKE_CXX_STANDARD 17)
include_directories(${CMAKE_JS_INC})
file(GLOB SOURCE_FILES "src/*.cc" "src/*.h")
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
set(JSON_BuildTests OFF CACHE INTERNAL "")
# Include N-API wrappers
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})
target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})
可以说非常简单易用了。
Api 使用
C++ 官方文档 的介绍还是比较简陋的,如果不了解 C-API 用起来有些地方会比较困惑。 但是如果用过之前用过 v8 的 addon-api 的话,还是比较容易理解的。
大部分的 Object 和 Array 等操作都有,你甚至可以用 ObjectWrap 去定义自己 的 Class:
class Example : public Napi::ObjectWrap<Example> {
public:
static Napi::Object Init(Napi::Env env, Napi::Object exports);
Example(const Napi::CallbackInfo &info);
private:
static Napi::FunctionReference constructor;
double _value;
Napi::Value GetValue(const Napi::CallbackInfo &info);
Napi::Value SetValue(const Napi::CallbackInfo &info);
};
我目前没有发现的就是如何定义一个 Undefined 的 Value。所以用到 Undefined
的时候,我就不得不用 C-API 去定义一个 napi_value
来用 undefined。好在
C++ 的 API 可以和 C 混用:
napi_value* result = nullptr;
if (napi_get_undefined(env, result) == napi_ok) {
// do something
}
在 js 里面的使用也很简单,不过要配合 bindings 库使用:
const example = require('bindings')('example');