目录背景介绍为什么要写 node addonnode addon 是什么addon 实现方式的变迁Chrome V8 apiNAN 时代符合 ABI 的 N-API编码阶段如何写出正
试想这样一种场景:我们想在 js 层实现某个业务场景,但是这套业务逻辑已经有存在的 c++ 版本了,这个时候我们有两个选择
node addon
桥接 C++
版本代码对比以上两种方案,显然,使用 addon
不用去写过重的业务逻辑,是一种成本更低的方案
node addon
,即为 node
插件 / 扩展,插件是用 C++
编写的动态链接共享对象。
javascript
和 C/C++
库之间的接口。require
函数可以将插件加载为普通的 node.js
模块。c++
和 js
的中间转换层可通过 NODE-API
、NAN
、或者使用底层 v8
库来实现【官方建议使用 NODE-API
】
node-api
:构建原生插件的 api,独立于 JS 运行时,此 API 是跨 Node.js 版本稳定的应用程序二进制接口,它旨在将插件与底层 JavaScript 引擎中的更改隔离开来,并允许为一个主要版本编译的模块nan(Native Abstractions for Node.js)
:是一个 Node.js
原生模块抽象接口集。它提供了一套 API
V8
:就是我们熟悉的 Chrome V8
1、是啥:即使用 Node
自身各种 API
以及 Chrome V8
的 API
2、存在的问题
这些写好的代码只能在特定的 Node 版本下编译,因为其中各种 API、函数声明等的变化会很大,举个例子
Handle<Value> Echo(const Arguments& args); // 0.10.x
void Echo(FunctionCallbackInfo<value>& args); // 6.x
1、是啥:
Native Abstractions for Node.js
,即 Node.js
原生模块抽象接口集NAN
的升级做改变,它会帮我们兼容各个版本2、存在的问题
Node.js
下也需要重新编译,如果版本不符合,Node.js
就无法正常载入一个 C++
扩展
1、是啥
2、N-API 的使用姿势
3、node-addon-api 是啥?
// N-API
#include <assert.h>
#include <node_api.h>
static napi_value Method(napi_env env, napi_callback_info info) {
napi_status status;
napi_value world;
status = napi_create_string_utf8(env, "world", 5, &world);
assert(status == napi_ok);
return world;
}
#define DECLARE_NAPI_METHOD(name, func) \
{ name, 0, func, 0, 0, 0, napi_default, 0 }
static napi_value Init(napi_env env, napi_value exports) {
napi_status status;
napi_property_descriptor desc = DECLARE_NAPI_METHOD("hello", Method);
status = napi_define_properties(env, exports, 1, &desc);
assert(status == napi_ok);
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
// node-addon-api
#include <napi.h>
Napi::String Method(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
return Napi::String::New(env, "world");
}
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set(Napi::String::New(env, "hello"),
Napi::Function::New(env, Method));
return exports;
}
NODE_API_MODULE(hello, Init)
demo.cc
1、熟悉 C++ 基础语法
宏的定义:#define 是定义一个宏的指令(预编译指令),它用来将一个标识符定义为一个字符串,该标识符被称为宏,被定义的字符串被称为替换文本,
// 简单的宏定义
#define PI 1415926 // 宏名 字符串
// 带参数的宏定义
#define A(x) x // 宏名(参数表) 宏体
// test.h
class Test : public B { // private || protected
public:
private:
protected:
int pro = 1;
}
// 类外
#include "test.h"
Test test; // 实例化 Test 类
std::cout << test.pro << std::endl; // error -> 不可在类外被访问
2、熟悉 addon 语法
1、如何让 js require?无后缀情况下的 .js -> .JSON -> .node
Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
return Link::Init(env, exports);
}
NODE_API_MODULE(link, InitAll);
2、定义一个类以及注册方法
Napi::Object Link::Init(Napi::Env env, Napi::Object exports) {
Napi::Function func =
DefineClass(
env, "Demo",
{
InstanceMethod("add", &Demo::Add),
}
);
auto constructor = Napi::Persistent(func);
constructor.SuppressDestruct();
exports.Set("Demo", func);
return exports;
}
3、函数的接收参数
// 1、定义好参数接收
Napi::Object Link::Init(Napi::Env env, Napi::Object exports) {}
// 2、在 CallbackInfo 中接收
Napi::Value Link::TagSync(const Napi::CallbackInfo &info) {
string bizId = info[0].As<Napi::String>();
auto tags = info[1].As<Napi::Array>();
}
ApplicationInfo applicationInfo = ParseValueAsApplicationInfo(info[0]);
kwai::link::ApplicationInfo ParseValueAsApplicationInfo(Napi::Value value) {
kwai::link::ApplicationInfo applicationInfo;
auto object = value.As<Napi::Object>();
applicationInfo.app_id = GetObjectValueAsInt32(object, "appId");
return applicationInfo;
}
int32_t GetObjectValueAsInt32(Napi::Object object, std::string keyName) {
if (object.Get(keyName).IsNumber()) {
return object.Get(keyName).ToNumber().Int32Value();
}
return 0;
}
4、函数的返回值
5、env
3、熟悉业务逻辑
有了上面两个知识储备后,下一步我们就要根据实际的业务场景,去写 addon 逻辑了
这个例子可结合上面的 demo.cc 和 demo.h 来一起看
Value runSimpleAsyncWorker(const CallbackInfo& info) {
int runTime = info[0].As<Number>();
Function callback = info[1].As<Function>();
SimpleAsyncWorker* asyncWorker = new SimpleAsyncWorker(callback, runTime);
asyncWorker->Queue();
std::string msg =
"SimpleAsyncWorker for " + std::to_string(runTime) + " seconds queued.";
return String::New(info.Env(), msg.c_str());
};
Object Init(Env env, Object exports) {
exports["runSimpleAsyncWorker"] = Function::New(
env, runSimpleAsyncWorker, std::string("runSimpleAsyncWorker"));
return exports;
}
NODE_API_MODULE(addon, Init)
使用 node-gyp 来构建,最终产出 .node
文件
1、第一步安装所需依赖
npm i node-gyp -g
2、第二步配置 binding.gyp
{
"targets": [
{
"target_name": "demo",
"cflags!": [ "-fno-exceptions" ],
"cflags_cc!": [
"-Wc++11-extensions"
],
"sources": [
"./src/simple_async_worker.cc",
"./src/addon.cc",
],
"include_dirs": [
"<!@(node -p \"require('node-addon-api').include\")",
"./",
],
'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],
"conditions": [
[
'OS=="Mac"',
{
"link_settings": {
"libraries": [
# 可引入一个静态库
]
},
"xcode_settings": {
"OTHER_CFLAGS": [ "-std=c++17", "-fexceptions", ],
},
'defines': [
'MACOS',
],
"cflags_cc": [
"-std=c++17"
]
}
],
]
},
],
}
3、执行 node-gyp rebuild
命令即可生成 require
方法可引入的 .node
文件
根据以上步骤,可实现一个极为简单的 node addon
扩展,但是在实际开发过程中,会面临更多的问题解决,欢迎讨论~
到此这篇关于Node中完整的 node addon 实现流程的文章就介绍到这了,更多相关node addon 流程内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
--结束END--
本文标题: Node中完整的 node addon 实现流程
本文链接: https://lsjlt.com/news/168234.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-01-12
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0