欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Node中完整的?node?addon?實(shí)現(xiàn)流程

 更新時(shí)間:2022年09月23日 08:29:44   作者:小小晴_  
這篇文章主要介紹了Node中完整的node?addon實(shí)現(xiàn)流程,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下

背景介紹

為什么要寫 node addon

試想這樣一種場(chǎng)景:我們想在 js 層實(shí)現(xiàn)某個(gè)業(yè)務(wù)場(chǎng)景,但是這套業(yè)務(wù)邏輯已經(jīng)有存在的 C++ 版本了,這個(gè)時(shí)候我們有兩個(gè)選擇

  • 重新實(shí)現(xiàn)一套在 JS 版本的業(yè)務(wù)場(chǎng)景
  • 使用 node addon 橋接 C++ 版本代碼

對(duì)比以上兩種方案,顯然,使用 addon 不用去寫過重的業(yè)務(wù)邏輯,是一種成本更低的方案

node addon 是什么

  • node addon,即為 node 插件 / 擴(kuò)展,插件是用 C++ 編寫的動(dòng)態(tài)鏈接共享對(duì)象。
    • 動(dòng)態(tài)鏈接共享對(duì)象,即動(dòng)態(tài)鏈接庫(kù)
    • 鏈接庫(kù):庫(kù)文件的二進(jìn)制版本,即將庫(kù)文件進(jìn)行編譯、打包操作后得到二進(jìn)制文件,無法獨(dú)立運(yùn)行,必須等待其他程序調(diào)用才會(huì)被載入內(nèi)存中。
    • 靜態(tài)鏈接:無論缺失的地址位于其他目標(biāo)文件還是鏈接庫(kù),鏈接庫(kù)都會(huì)逐個(gè)找到各目標(biāo)文件中缺失的地址。采用此鏈接方式生成的可執(zhí)行文件,可以獨(dú)立載入內(nèi)存運(yùn)行。
    • 動(dòng)態(tài)鏈接:鏈接器先從所有目標(biāo)文件中找到部分缺失的地址,然后將所有目標(biāo)文件組織成一個(gè)可執(zhí)行文件。這樣生成的可執(zhí)行文件,仍缺失部分函數(shù)和變量地址,待文件執(zhí)行時(shí),需連同所有的鏈接庫(kù)文件一起載入內(nèi)存,再由鏈接器完成剩余的地址修復(fù)工作,才能正常執(zhí)行
    • 靜態(tài)鏈接庫(kù):在生成可執(zhí)行文件之前完成所有鏈接操作,使用的庫(kù)文件是靜態(tài)鏈接庫(kù),后綴名 .a .lib
    • 動(dòng)態(tài)鏈接庫(kù):將部分鏈接操作推遲到程序執(zhí)行時(shí)才進(jìn)行,使用的庫(kù)文件是動(dòng)態(tài)鏈接庫(kù),后綴名:.so .dll .dylib
  • 插件提供了 JavaScript 和 C/C++ 庫(kù)之間的接口。
  • require 函數(shù)可以將插件加載為普通的 Node.js 模塊。
  • 通俗點(diǎn)來講,是一個(gè)能夠橋接 c++ 和 js 的中間轉(zhuǎn)換層

可通過 NODE-API、NAN、或者使用底層 v8 庫(kù)來實(shí)現(xiàn)【官方建議使用 NODE-API

  • node-api:構(gòu)建原生插件的 api,獨(dú)立于 JS 運(yùn)行時(shí),此 API 是跨 Node.js  版本穩(wěn)定的應(yīng)用程序二進(jìn)制接口,它旨在將插件與底層 JavaScript 引擎中的更改隔離開來,并允許為一個(gè)主要版本編譯的模塊
  • nan(Native Abstractions for Node.js):是一個(gè) Node.js 原生模塊抽象接口集。它提供了一套 API
  • 底層 V8:就是我們熟悉的 Chrome V8

addon 實(shí)現(xiàn)方式的變遷

Chrome V8 API

1、是啥:即使用 Node 自身各種 API 以及 Chrome V8 的 API

2、存在的問題

這些寫好的代碼只能在特定的 Node 版本下編譯,因?yàn)槠渲懈鞣N API、函數(shù)聲明等的變化會(huì)很大,舉個(gè)例子

Handle<Value> Echo(const Arguments& args);    // 0.10.x
void Echo(FunctionCallbackInfo<value>& args); // 6.x

NAN 時(shí)代

1、是啥:

  • Native Abstractions for Node.js,即 Node.js 原生模塊抽象接口集
  • 代碼只需要隨著 NAN 的升級(jí)做改變,它會(huì)幫我們兼容各個(gè)版本

2、存在的問題

  • 一次寫好的代碼在不用版本的 Node.js 下也需要重新編譯,如果版本不符合,Node.js 就無法正常載入一個(gè) C++ 擴(kuò)展
    • NAN 的封裝方式是使用了一堆宏,在不同的 Node 版本下使用不同的宏變量、函數(shù)等,所以針對(duì)用戶使用不同的 Node 版本,是需要進(jìn)行重新編譯的

符合 ABI 的 N-API

1、是啥

  • 自從 2017 年 Node.js v8.0.0 發(fā)布之后,Node.js 推出了全新的用于開發(fā) C++ 原生模塊的接口-> N-API
  • 與 NAN 相比,它把 Node.js 的所有底層數(shù)據(jù)結(jié)構(gòu)全部黑盒化,抽象成 N-API 中的接口;不同版本的 Node.js 使用同樣的接口,這些接口穩(wěn)定且 ABI 化。只要 ABI 版本號(hào)一致,編譯好的 C++ 擴(kuò)展就可以直接使用,而不需要重新編譯
    • ABI 化:(Application Binary Interface)應(yīng)用程序二進(jìn)制接口;可以理解為一種約定,是 API 的編譯版本;ABI 允許編譯好的目標(biāo)代碼在使用兼容 ABI 的系統(tǒng)中無需改動(dòng)就能運(yùn)行;一套完整的 ABI 可以讓程序在所有支持該 ABI 的系統(tǒng)上運(yùn)行,無需對(duì)程序進(jìn)行修改
    • API:(Application programming interface),應(yīng)用編程接口
  • 主要收益:消除了 Nodejs 版本之間的差異

2、N-API 的使用姿勢(shì)

3、node-addon-api 是啥?

  • 可以理解為是對(duì) N-API 更進(jìn)一步的封裝,更加便于我們開發(fā)
  • 舉個(gè)例子
// 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.h

demo.cc 

1、熟悉 C++ 基礎(chǔ)語(yǔ)法

宏的定義:#define 是定義一個(gè)宏的指令(預(yù)編譯指令),它用來將一個(gè)標(biāo)識(shí)符定義為一個(gè)字符串,該標(biāo)識(shí)符被稱為宏,被定義的字符串被稱為替換文本,

  • 簡(jiǎn)單的宏定義和帶參數(shù)的宏定義
  • 當(dāng)宏出現(xiàn)在一個(gè)文件中時(shí),在該文件后續(xù)出現(xiàn)的所有宏都將被替換為 《替換文本》
  • 常用于條件編譯情況下,比如版本號(hào)的不同而編譯不同的邏輯
// 簡(jiǎn)單的宏定義
#define PI 1415926  // 宏名 字符串

// 帶參數(shù)的宏定義
#define A(x) x   // 宏名(參數(shù)表) 宏體
    • 公有繼承、私有繼承、保護(hù)繼承
      • 公有繼承:繼承父類 public 和 protected 的方法和變量,不能訪問 private
      • 私有繼承:繼承父類的 public 和 protected 的方法和變量作為私有成員,不能被該類的子類再次訪問
      • 保護(hù)繼承:繼承父類 public 和 protected 成員作為保護(hù)成員
    • 公有、私有、受保護(hù)的成員
      • public:可被子類繼承或在類外內(nèi)訪問
      • private:僅限類內(nèi)部使用,不可被繼承和訪問
      • protected: 可被子類繼承,但不能在類外訪問
// test.h
class Test : public B {  // private || protected
	public:
  private:
  protected:
  	int pro = 1;
}
// 類外
#include "test.h"
Test test;   // 實(shí)例化 Test 類
std::cout << test.pro << std::endl;  // error -> 不可在類外被訪問
  • 構(gòu)造函數(shù)和析構(gòu)函數(shù)
    • 構(gòu)造函數(shù):類的構(gòu)造函數(shù)是類的一種特殊的成員函數(shù),它會(huì)在每次創(chuàng)建類的新對(duì)象時(shí)執(zhí)行。類似于 JS 中的 constructor; 可自己實(shí)現(xiàn),也可使用編譯器生成的默認(rèn)構(gòu)造函數(shù),即與類名相同的函數(shù);
    • 析構(gòu)函數(shù):類的析構(gòu)函數(shù)是類的一種特殊的成員函數(shù),它會(huì)在每次刪除所創(chuàng)建的對(duì)象時(shí)執(zhí)行。析構(gòu)函數(shù)的名稱與類的名稱是完全相同的,只是在前面加了個(gè)波浪號(hào)(~)作為前綴,它不會(huì)返回任何值,也不能帶有任何參數(shù)。析構(gòu)函數(shù)有助于在跳出程序(比如關(guān)閉文件、釋放內(nèi)存等)前釋放資源。
  • 虛函數(shù)、純虛函數(shù)是啥
    • virtual:虛函數(shù)關(guān)鍵字,子類可選擇自己實(shí)現(xiàn)或使用父類原有方法
    • = 0:純虛函數(shù)關(guān)鍵字,子類必須自己實(shí)現(xiàn),如果不實(shí)現(xiàn),編譯階段將會(huì)報(bào)錯(cuò)
    • override:是一個(gè)覆蓋虛函數(shù)的標(biāo)識(shí)符

2、熟悉 addon 語(yǔ)法

1、如何讓 js require?無后綴情況下的 .js -> .json -> .node

  • 在 js 中,使用 commonJs 語(yǔ)法即可讓該模塊被其他模塊 require,addon 中則也是類似的想法
  • 在 addon 中,提供了 NODE_API_MODULE 宏方法,用這個(gè)方法即可實(shí)現(xiàn)外部 require 效果,方法接收兩個(gè)參數(shù),即模塊名字和導(dǎo)出的方法
  • 具體實(shí)現(xiàn)?
Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
  return Link::Init(env, exports);
}
NODE_API_MODULE(link, InitAll);

2、定義一個(gè)類以及注冊(cè)方法

  • 在 js 中的效果即為 class A { //.... }
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、函數(shù)的接收參數(shù)

  • 在 addon 中
    • 接收多個(gè)參數(shù):
    • 接收一個(gè)對(duì)象
      • 也是從 info[0] 中去拿到這個(gè)對(duì)象,然后用 object.Get(key) 方法拿到對(duì)應(yīng)的參數(shù)
// 1、定義好參數(shù)接收
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、函數(shù)的返回值

  • 類型約束:在函數(shù)前約束,類型可寫 addon 類型或者原生 C++ 類型
  • 和 js 一樣,寫個(gè) return 就可以了

5、env

  • 是什么
    • 可以理解為是 node addon 運(yùn)行時(shí)的請(qǐng)求環(huán)境
    • Env 對(duì)象通常由 Node.js 運(yùn)行時(shí)或 node-addon-api 基礎(chǔ)結(jié)構(gòu)創(chuàng)建和傳遞
  • 怎么用:github.com/nodejs/node…
    • 類型聲明:Napi::Env
    • 取值:info.Env()
  • 為什么需要構(gòu)建這個(gè)環(huán)境

3、熟悉業(yè)務(wù)邏輯

有了上面兩個(gè)知識(shí)儲(chǔ)備后,下一步我們就要根據(jù)實(shí)際的業(yè)務(wù)場(chǎng)景,去寫 addon 邏輯了

如何向外暴露方法

這個(gè)例子可結(jié)合上面的 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 來構(gòu)建,最終產(chǎn)出 .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": [
                        # 可引入一個(gè)靜態(tài)庫(kù)
                    ]
                                        },
                                        "xcode_settings": {
                                                "OTHER_CFLAGS": [ "-std=c++17", "-fexceptions", ],
                                        },
                                        'defines': [
                                                'MACOS',
                                        ],
                                        "cflags_cc": [
                                                "-std=c++17"
                                        ]
                                }
                        ],
                ]
        },
],
}

3、執(zhí)行 node-gyp rebuild 命令即可生成 require 方法可引入的 .node 文件

結(jié)語(yǔ)

根據(jù)以上步驟,可實(shí)現(xiàn)一個(gè)極為簡(jiǎn)單的 node addon 擴(kuò)展,但是在實(shí)際開發(fā)過程中,會(huì)面臨更多的問題解決,歡迎討論~

到此這篇關(guān)于Node中完整的 node addon 實(shí)現(xiàn)流程的文章就介紹到這了,更多相關(guān)node addon 流程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關(guān)文章

  • 詳解webpack打包nodejs項(xiàng)目(前端代碼)

    詳解webpack打包nodejs項(xiàng)目(前端代碼)

    這篇文章主要介紹了webpack打包nodejs項(xiàng)目(前端代碼),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-09-09
  • 從零學(xué)習(xí)node.js之express入門(六)

    從零學(xué)習(xí)node.js之express入門(六)

    相信大家都知道Express是一個(gè)簡(jiǎn)潔而靈活的 node.js Web應(yīng)用框架, 提供了一系列強(qiáng)大特性幫助你創(chuàng)建各種 Web 應(yīng)用,和豐富的 HTTP 工具。下面這篇文章主要介紹了node.js中express的入門知識(shí),需要的朋友可以參考下。
    2017-02-02
  • NodeJs實(shí)現(xiàn)簡(jiǎn)單的爬蟲功能案例分析

    NodeJs實(shí)現(xiàn)簡(jiǎn)單的爬蟲功能案例分析

    爬蟲,是一種按照一定的規(guī)則,自動(dòng)地抓取網(wǎng)頁(yè)信息的程序或者腳本。這篇文章通過一個(gè)案例給大家分享NodeJs實(shí)現(xiàn)簡(jiǎn)單的爬蟲功能,感興趣的朋友一起看看吧
    2018-12-12
  • 說說node中的可讀流和可寫流的區(qū)別

    說說node中的可讀流和可寫流的區(qū)別

    這篇文章主要介紹了說說node中的可讀流和可寫流的區(qū)別,詳細(xì)的介紹了可讀流和可寫流,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-06-06
  • windows系統(tǒng)下安裝npm(Node.js)方法教程

    windows系統(tǒng)下安裝npm(Node.js)方法教程

    在Windows環(huán)境下進(jìn)行Node.js的安裝并不是一件復(fù)雜的事情,但是在安裝過程中需要注意一些細(xì)節(jié),下面這篇文章主要給大家介紹了關(guān)于windows系統(tǒng)下安裝npm(Node.js)的相關(guān)資料,需要的朋友可以參考下
    2023-12-12
  • node.js中的fs.realpathSync方法使用說明

    node.js中的fs.realpathSync方法使用說明

    這篇文章主要介紹了node.js中的fs.realpathSync方法使用說明,本文介紹了fs.realpathSync的方法說明、語(yǔ)法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下
    2014-12-12
  • NodeJS中的命令行程序、工程目錄、NPM

    NodeJS中的命令行程序、工程目錄、NPM

    使用NodeJS編寫的東西,要么是一個(gè)包,要么是一個(gè)命令行程序,而前者最終也會(huì)用于開發(fā)后者,一般我們會(huì)同時(shí)提供命令行模式和API模式兩種使用方式,并且我們會(huì)借助三方包來編寫代碼,NPM是隨同NodeJS一起安裝的包管理工具,能解決NodeJS代碼部署上的很多問題
    2023-11-11
  • Node.js安裝及npm國(guó)內(nèi)鏡像配置的方法實(shí)現(xiàn)

    Node.js安裝及npm國(guó)內(nèi)鏡像配置的方法實(shí)現(xiàn)

    本文主要介紹了Node.js安裝及npm國(guó)內(nèi)鏡像配置,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • 使用Jasmine和Karma對(duì)AngularJS頁(yè)面程序進(jìn)行測(cè)試

    使用Jasmine和Karma對(duì)AngularJS頁(yè)面程序進(jìn)行測(cè)試

    這篇文章主要介紹了使用Jasmine和Karma對(duì)AngularJS頁(yè)面程序進(jìn)行測(cè)試的方法,以Node.js為環(huán)境,非常適合JavaScript的全棧開發(fā)時(shí)使用,需要的朋友可以參考下
    2016-03-03
  • node.js中的fs.readFileSync方法使用說明

    node.js中的fs.readFileSync方法使用說明

    這篇文章主要介紹了node.js中的fs.readFileSync方法使用說明,本文介紹了fs.readFileSync的方法說明、語(yǔ)法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下
    2014-12-12

最新評(píng)論