編寫原生的Node.js模塊

導語:當Javascript的性能遭遇瓶頸,或者須要加強Javascript能力的時候,就須要依賴native模塊來實現了。javascript

應用場景

平常工做中,咱們常常須要將原生的Node.js模塊作爲依賴並在項目中進行使用。下面有個列表,你可能對它們的名字很熟悉:css

一般,咱們開發原生Node.js模塊包括但不只限於如下緣由:html

  • 對性能有比較苛刻要求的應用。儘管Node.js得益於libuv,在異步I/O操做頗有優點,但遇到數字計算時並非一個很好的選擇。
  • 使用更加底層的API,好比操做系統層面的。
  • 在C/C++和Node.js之間建立一個Bridge,進行通訊。

什麼是原生模塊?

Node.js Addons是動態連接的可共享對象,由C/C++編寫而成。能夠在Node.js中經過require()方法進行調用,使用起來像調用Node.js普通模塊同樣。 —— 來自Node.js官方文檔java

這意味着若是處理得當的話,模塊調用者使用由C/C++編寫的原生模塊的方式和由Node.js編寫的模塊同樣。想要編寫Node.js addons,你須要瞭解一些基本知識:node

推薦閱讀這些資料。python

建立Node.js的原生擴展模塊

下面我以一個常見的動態規劃問題-青蛙跳臺階爲例子來講明如何建立一個原生的Node.js模塊。青蛙跳臺階描述爲:一隻青蛙一次能夠跳上一級臺階,也能夠跳上2級臺階,求該青蛙跳上n級臺階的共有多少種跳法?linux

首先建立一個frog_jump.cc原生文件,.cc的意思是c with class,擴展名也能夠是.cpp。Google Style Guide建議使用.cc,那麼此處仍是以.cc作爲擴展名吧。代碼以下:c++

#include <node.h>
#include<vector>

/**
 * Native method, calculate all ways frog jump to a target stair.
 */
int climbStairs(int n) {
  std::vector<int> dp(n);

  dp[1] = 1;
  dp[2] = 2;

  for (int i = 3; i <= n; i ++ ) {
    dp[i] = dp[i - 1] + dp[i - 2];
  }
  return dp[n];
}

/**
 * Export native method jumpTo
 */
void JumpTo(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();

  // Check input type
  if (!args[0] -> IsNumber()) {
    isolate -> ThrowException(v8::Exception::TypeError(
      v8::String::NewFromUtf8(isolate, "Wrong arguments type!")));
  }

  int value = climbStairs(args[0] -> NumberValue());
  v8::Local<v8::Number> num = v8::Number::New(isolate, value);

  args.GetReturnValue().Set(num);
}

// init is entry point.
void init(v8::Local<v8::Object> exports) {
  NODE_SET_METHOD(exports, "jumpTo", JumpTo);
}

NODE_MODULE(frog_jump, init)

對這段代碼的解釋:git

  • #include "node.h" 是c++裏面引入頭文件的方式,具體源碼:node.h,C++連接時會加載這個頭文件。頭文件裏面引入了v8命名空間,咱們能夠經過v8::標誌來訪問v8的接口。訪問全部v8的類型,都須要使用v8::標誌
  • 經過args對象來訪問Node.js傳遞過來的參數,經過args也能夠獲取調用相關信息。
  • 經過v8::Isolate*能夠獲取函數做用域,能夠像JS裏面同樣進行變量賦值,而不用擔憂垃圾回收問題,垃圾回收器會自動進行。
  • args.GetReturnValue()能夠對函數返回的結果進行設置。
  • 任何原生Node.js模塊都須要調用NODE_MODULE,NODE_MODULE是一個宏,它會進行模塊註冊操做。
  • C++ 有豐富的內置類型來保存數字或者字符串,可是JS只能識別v8::裏面定義的類型。所以,將c++的變量賦值給JS時,須要轉換成能夠被JS識別的類型,也便是v8::定義的類型。好比v8::String、v8::Object。

編譯原生的Node.js模塊

一旦源代碼編寫完成,須要將它編譯成二進制的addon.node文件,以後才能被Node.js require。爲了完成編譯操做,須要在項目的根目錄建立binding.gyp文件,裏面定義了Build的配置。binding.gyp的內容是一個JSON。github

{
  "targets": [
    {
      "target_name": "frog_jump",
      "sources": [ "frog_jump.cc" ]
    }
  ]
}

編譯環境配置:

  • windows: 以管理員的身份運行npm install --global --production windows-build-tools,這個會安裝全部編譯依賴的工具。
  • linux: 安裝python v2.7makeGCC
  • osx: 安裝xcode

雖然npm內置了一個node-gyp版本,可是這個版本沒有開放給開發者進行調用。npm install的時候會調用它來進行編譯和安裝工做。所以,開發者想要調用node-gyp必須本身安裝一個全局的node-gyp版本。

$ npm install node-gyp -g
$ node-gyp configure
$ node-gyp build

運行node-gyp configure命令會生成一個跨平臺的build文件,unix環境會生成Makefile,windows環境會在build目錄裏面生成vcxproj。
運行node-gyp build命令會生成可被Node.js調動的addon.node二進制文件。

Node.js中調用原生模塊

const frogJump = require('./build/Release/frog_jump');

frogJump.jumpTo(20);  //青蛙跳到第20個臺階的全部方法

項目源代碼:frog-jump

後續

nan,即Native Abstractions for Node.js。它基於Node.js API接口,兼容全部Node版本,目前的最佳實踐是基於nan來擴展原生模塊,而不是直接使用Node.js API。
N-API,Node官方推出的用來編寫原生Node擴展模塊,是V8和nan的替代,目前處於實驗階段。

相關文章
相關標籤/搜索