# Addons插件
Addons插件就是動(dòng)態(tài)連接庫(kù)。它類似膠水,將c、c++和Node粘貼起來(lái)。它的API(目前來(lái)說(shuō))相當(dāng)復(fù)雜,涉及到了幾個(gè)類庫(kù)的知識(shí)。
- V8 JavaScript引擎,一個(gè) C++ 類庫(kù). 用于和JavaScript進(jìn)行交互的接口。 創(chuàng)建對(duì)象, 調(diào)用函數(shù)等. 文檔大部分在這里: `v8.h` 頭文件 (`deps/v8/include/v8.h`在Node源代碼目錄里), 也有可用的線上文檔 [線上](http://izs.me/v8-docs/main.html). (譯者:想要學(xué)習(xí)c++的addons插件編寫(xiě),必須先了解v8的接口)
- [libuv](https://github.com/joyent/libuv), C語(yǔ)言編寫(xiě)的事件循環(huán)類庫(kù)。任何時(shí)候需要等待一個(gè)文件描述符變?yōu)榭勺x狀態(tài),等待一個(gè)定時(shí)器,或者等待一個(gè)接受信號(hào)都需要使用libuv類庫(kù)的接口。也就是說(shuō),如果你執(zhí)行任何I/O操作,libuv類庫(kù)將會(huì)被用到。
- 內(nèi)部 Node 類庫(kù).最重要的接口就是 `node::ObjectWrap` 類,這個(gè)類你應(yīng)該是最可能想要派生的。
- 其他.請(qǐng)參閱 `deps/` 獲得更多可用類庫(kù)。
Node 靜態(tài)編譯了所有依賴到它的可執(zhí)行文件中去了。當(dāng)編譯你的模塊時(shí),你不必?fù)?dān)心無(wú)法連接上述那些類庫(kù)。 (譯者:換而言之,你在編譯自己的addons插件時(shí),只管在頭部 #include <uv.h>,不必在binding.gyp中聲明)
下面所有的例子都可以下載到: [下載](https://github.com/rvagg/node-addon-examples) 這或許能成為你學(xué)習(xí)和創(chuàng)作自己addon插件的起點(diǎn)。
### Hello world(世界你好)
作為開(kāi)始,讓我們用編寫(xiě)一個(gè)小的addon插件,這個(gè)addon插件的c++代碼相當(dāng)于下面的JavaScript代碼。
~~~
module.exports.hello = function() { return 'world'; };
~~~
首先我們創(chuàng)建一個(gè) `hello.cc`文件:
~~~
NODE_MODULE(hello, init)//譯者:將addon插件名hello和上述init函數(shù)關(guān)聯(lián)輸出
~~~
注意所有Node的addons插件都必須輸出一個(gè)初始化函數(shù):
~~~
void Initialize (Handle<Object> exports);
NODE_MODULE(module_name, Initialize)
~~~
在`NODE_MODULE`之后沒(méi)有分號(hào),因?yàn)樗皇且粋€(gè)函數(shù)(請(qǐng)參閱`node.h`)
這個(gè)`module_name`(模塊名)需要和最后編譯生成的2進(jìn)制文件名(減去.node后綴名)相同。
源代碼需要生成在`hello.node`,這個(gè)2進(jìn)制addon插件中。 需要做到這些,我們要?jiǎng)?chuàng)建一個(gè)名為`binding.gyp`的文件,它描述了創(chuàng)建這個(gè)模塊的配置,并且它的格式是類似JSON的。 文件將被命令:[node-gyp](https://github.com/TooTallNate/node-gyp) 編譯。
~~~
{
"targets": [
{
"target_name": "hello", //譯者:addon插件名,注意這里的名字必需和上面NODE_MODULE中的一致
"sources": [ "hello.cc" ] //譯者:這是需要編譯的源文件
}
]
}
~~~
下一步是根據(jù)當(dāng)前的操作系統(tǒng)平臺(tái),利用`node-gyp configure`命令,生成合適的項(xiàng)目文件。
現(xiàn)在你會(huì)有一個(gè)`Makefile` (在Unix平臺(tái)) 或者一個(gè) `vcxproj` file (在Windows上),它們都在`build/` 文件夾中. 然后執(zhí)行命令 `node-gyp build`進(jìn)行編譯。 (譯者:當(dāng)然你可以執(zhí)行 `node-gyp rebuild`一步搞定)
現(xiàn)在你已經(jīng)有了編譯好的 `.node` 文件了,這個(gè)編譯好的綁定文件會(huì)在目錄 `build/Release/`下
現(xiàn)在你可以使用這個(gè)2進(jìn)制addon插件在Node項(xiàng)目`hello.js` 中了,通過(guò)指明`require`這個(gè)剛剛創(chuàng)建的`hello.node`模塊使用它。
~~~
console.log(addon.hello()); // 'world'
~~~
請(qǐng)閱讀下面的內(nèi)容獲得更多詳情或者訪問(wèn)[https://github.com/arturadib/node-qt](https://github.com/arturadib/node-qt)獲取一個(gè)生產(chǎn)環(huán)境的例子。
### Addon patterns(插件方式)
下面是一些幫助你開(kāi)始編寫(xiě)addon插件的方式。參考這個(gè)在線的[v8 手冊(cè)](http://izs.me/v8-docs/main.html)用來(lái)幫助你調(diào)用各種v8接口, 然后是v8的 [嵌入式開(kāi)發(fā)向?qū)(http://code.google.com/apis/v8/embed.html) ,解釋幾個(gè)概念,如 handles, scopes,function templates等。
為了能跑起來(lái)這些例子,你必須用 `node-gyp` 來(lái)編譯他們。 創(chuàng)建一個(gè)`binding.gyp` 文件:
~~~
{
"targets": [
{
"target_name": "addon",
"sources": [ "addon.cc" ]
}
]
}
~~~
事實(shí)上可以有多個(gè) `.cc` 文件, 就簡(jiǎn)單的在 `sources` 數(shù)組里加上即可,例子:
~~~
"sources": ["addon.cc", "myexample.cc"]
~~~
現(xiàn)在你有了你的`binding.gyp`文件了,你可要開(kāi)始執(zhí)行configure 和 build 命令構(gòu)建你的addon插件了
~~~
$ node-gyp configure build
~~~
### Function arguments(函數(shù)參數(shù))
下面的部分說(shuō)明了如何從JavaScript的函數(shù)調(diào)用獲得參數(shù)然后返回一個(gè)值。這是主要的內(nèi)容并且僅需要源代碼`addon.cc`。
~~~
NODE_MODULE(addon, Init)
~~~
你可以使用下面的JavaScript代碼片段來(lái)測(cè)試它
~~~
console.log( 'This should be eight:', addon.add(3,5) );
~~~
### Callbacks(回調(diào))
你可以傳遞JavaScript functions 到一個(gè)C++ function 并且執(zhí)行他們,這里是 `addon.cc`文件:
~~~
NODE_MODULE(addon, Init)
~~~
注意這個(gè)例子對(duì)`Init()`使用了兩個(gè)參數(shù),將完整的 `module` 對(duì)象作為第二個(gè)參數(shù)傳入。這允許addon插件完全的重寫(xiě) `exports`,這樣就可以用一個(gè)函數(shù)代替多個(gè)函數(shù)作為`exports`的屬性了。
你可以使用下面的JavaScript代碼片段來(lái)測(cè)試它
~~~
addon(function(msg){
console.log(msg); // 'hello world'
});
~~~
### Object factory(對(duì)象工廠)
在這個(gè)`addon.cc`文件里用一個(gè)c++函數(shù),你可以創(chuàng)建并且返回一個(gè)新的對(duì)象,這個(gè)新的對(duì)象擁有一個(gè)msg的屬性,它的值是通過(guò)createObject()方法傳入的
~~~
NODE_MODULE(addon, Init)
~~~
在js中測(cè)試如下:
~~~
var obj1 = addon('hello');
var obj2 = addon('world');
console.log(obj1.msg+' '+obj2.msg); // 'hello world'
~~~
### Function factory(函數(shù)工廠)
這次將展示如何創(chuàng)建并返回一個(gè)JavaScript function函數(shù),這個(gè)函數(shù)其實(shí)是通過(guò)c++包裝的。
~~~
NODE_MODULE(addon, Init)
~~~
測(cè)試它:
~~~
var fn = addon();
console.log(fn()); // 'hello world'
~~~
### Wrapping C++ objects(包裝c++對(duì)象)
這里將創(chuàng)建一個(gè)被c++包裹的對(duì)象或類`MyObject`,它是可以在JavaScript中通過(guò)`new`操作符實(shí)例化的。 首先我們要準(zhǔn)備主要的模塊文件`addon.cc`:
~~~
NODE_MODULE(addon, InitAll)
~~~
然后在`myobject.h`文件中創(chuàng)建你的包裝類,它繼承自 `node::ObjectWrap`:
~~~
#endif
~~~
在文件 `myobject.cc` 可以實(shí)施各種你想要暴露給js的方法。 這里我們暴露方法名為 `plusOne`給就是,它表示將構(gòu)造函數(shù)的屬性加1.
~~~
return scope.Close(Number::New(obj->counter_));
}
~~~
測(cè)試它:
~~~
var obj = new addon.MyObject(10);
console.log( obj.plusOne() ); // 11
console.log( obj.plusOne() ); // 12
console.log( obj.plusOne() ); // 13
~~~
### Factory of wrapped objects(工廠包裝對(duì)象)
這是非常有用的,當(dāng)你想創(chuàng)建原生的JavaScript對(duì)象時(shí),又不想明確的使用JavaScript的`new`操作符。
~~~
var obj = addon.createObject();
// 用上面的方式代替下面的:
// var obj = new addon.Object();
~~~
讓我們注冊(cè)在 `addon.cc` 文件中注冊(cè)`createObject`方法:
~~~
NODE_MODULE(addon, InitAll)
~~~
在`myobject.h`文件中,我們現(xiàn)在介紹靜態(tài)方法NewInstance`,它能夠?qū)嵗瘜?duì)象(舉個(gè)例子,它的工作就像是 在JavaScript中的`new` 操作符。)
~~~
#endif
~~~
這里的處理方式和上面的 `myobject.cc`很像:
~~~
return scope.Close(Number::New(obj->counter_));
}
~~~
測(cè)試它:
~~~
var obj2 = createObject(20);
console.log( obj2.plusOne() ); // 21
console.log( obj2.plusOne() ); // 22
console.log( obj2.plusOne() ); // 23
~~~
### Passing wrapped objects around(傳遞包裝的對(duì)象)
除了包裝和返回c++對(duì)象以外,你可以傳遞他們并且通過(guò)Node的`node::ObjectWrap::Unwrap`幫助函數(shù)解包裝他們。 在下面的`addon.cc` 文件中,我們介紹了一個(gè)函數(shù)`add()`,它能夠獲取2個(gè)`MyObject`對(duì)象。
~~~
NODE_MODULE(addon, InitAll)
~~~
為了使事情變得有趣,我們?cè)?`myobject.h` 采用一個(gè)公共的方法,所以我們能夠在unwrapping解包裝對(duì)象之后使用私有成員的值。
~~~
#endif
~~~
`myobject.cc`文件的處理方式和前面類似
~~~
return scope.Close(instance);
}
~~~
測(cè)試它:
~~~
var obj1 = addon.createObject(10);
var obj2 = addon.createObject(20);
var result = addon.add(obj1, obj2);
~~~
~~~
console.log(result); // 30
~~~
- 關(guān)于本文檔
- 概述
- 斷言 (assert)
- Buffer
- Addons插件
- 子進(jìn)程
- 集群
- 控制臺(tái)
- 加密(Crypto)
- 調(diào)試器
- DNS
- 域
- 事件 (Events)
- File System
- 全局對(duì)象
- HTTP
- HTTPS
- Modules
- net
- 操作系統(tǒng)
- 路徑 (Path)
- process
- punycode
- Query String
- Readline
- REPL
- Smalloc
- 流
- 字符串解碼器
- 定時(shí)器
- TLS (SSL)
- TTY
- UDP / 數(shù)據(jù)報(bào)套接字
- URL
- utils
- 執(zhí)行 JavaScript
- Zlib
- 進(jìn)度
- 感謝
