node.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > node.js > node addon 流程

Node中完整的 node addon 实现流程

作者:小小晴_

这篇文章主要介绍了Node中完整的node addon实现流程,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下

背景介绍

为什么要写 node addon

试想这样一种场景:我们想在 js 层实现某个业务场景,但是这套业务逻辑已经有存在的 C++ 版本了,这个时候我们有两个选择

对比以上两种方案,显然,使用 addon 不用去写过重的业务逻辑,是一种成本更低的方案

node addon 是什么

可通过 NODE-APINAN、或者使用底层 v8 库来实现【官方建议使用 NODE-API

addon 实现方式的变迁

Chrome V8 API

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

NAN 时代

1、是啥:

2、存在的问题

符合 ABI 的 N-API

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) 

编码阶段

如何写出正确的 addon 逻辑

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 流程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文