Rumah > hujung hadapan web > tutorial js > Cara yang betul untuk menulis Node.js plug-ins_node.js

Cara yang betul untuk menulis Node.js plug-ins_node.js

WBOY
Lepaskan: 2016-05-16 16:40:26
asal
1304 orang telah melayarinya

Node.js cemerlang dalam menggunakan JavaScript untuk menulis hujung belakang, dan ia patut dicuba lebih banyak lagi. Walau bagaimanapun, jika anda memerlukan beberapa fungsi yang tidak boleh digunakan secara langsung atau modul yang tidak boleh dilaksanakan sama sekali, bolehkah anda memperkenalkan hasil seperti itu daripada perpustakaan C/C? Jawapannya ya, anda hanya perlu menulis pemalam dan menggunakan sumber perpustakaan kod lain dalam kod JavaScript anda sendiri. Mari kita mulakan perjalanan siasatan hari ini bersama-sama.

Pengenalan

Seperti yang dinyatakan dalam dokumentasi rasmi Node.js, pemalam ialah objek kongsi yang dipautkan secara dinamik dan boleh menyambungkan kod JavaScript dengan perpustakaan C/C. Ini bermakna kita boleh merujuk apa sahaja daripada perpustakaan C/C++ dan memasukkannya ke dalam Node.js dengan mencipta pemalam.

Sebagai contoh, kami akan mencipta pembungkus untuk objek std::string standard.

Persediaan

Sebelum kami mula menulis, anda perlu terlebih dahulu memastikan bahawa anda telah menyediakan semua bahan yang diperlukan untuk penyusunan modul seterusnya. Anda memerlukan nod-gyp dan semua kebergantungannya. Anda boleh menggunakan arahan berikut untuk memasang node-gyp:

npm install -g node-gyp
Salin selepas log masuk

Dari segi kebergantungan, kita perlu menyediakan item berikut untuk sistem Unix: • Python (memerlukan versi 2.7, 3.x tidak akan berfungsi dengan betul)

• buat

• Rantai alat pengkompil A C (seperti gpp atau g)

Sebagai contoh, pada Ubuntu anda boleh menggunakan arahan berikut untuk memasang semua projek di atas (Python 2.7 sepatutnya telah diprapasang):

sudo apt-get install build-essentials 
Salin selepas log masuk

Dalam persekitaran sistem Windows, perkara yang anda perlukan ialah:

• Python (versi 2.7.3, 3.x tidak berfungsi dengan betul)

• Microsoft Visual Studio C 2010 (untuk Windows XP/Vista)

• Microsoft Visual Studio C 2012 untuk Windows Desktop (untuk Windows 7/8)

Saya menekankan bahawa versi Express Visual Studio juga akan berfungsi seperti biasa.

fail binding.gyp

Fail ini digunakan oleh node-gyp untuk menjana fail binaan yang sesuai untuk pemalam kami. Anda boleh klik di sini untuk melihat dokumentasi fail .gyp yang disediakan oleh Wikipedia, tetapi contoh yang akan kami gunakan hari ini adalah sangat mudah, jadi hanya gunakan kod berikut:

{ 
  "targets": [ 
    { 
      "target_name": "stdstring", 
      "sources": [ "addon.cc", "stdstring.cc" ] 
    } 
  ] 
}
Salin selepas log masuk

Nama_sasaran boleh ditetapkan kepada apa sahaja yang anda suka. Tatasusunan sumber mengandungi semua fail sumber yang perlu digunakan oleh pemalam. Dalam contoh kami, kami juga menyertakan addon.cc, yang digunakan untuk mengandungi kod yang diperlukan untuk menyusun pemalam dan stdstring.cc, serta kelas pembalut kami.

Kelas STDSStringWrapper

Langkah pertama ialah menentukan kelas kita sendiri dalam fail stdstring.h. Jika anda biasa dengan pengaturcaraan C, maka anda pasti tidak akan asing dengan dua baris kod berikut.

#ifndef STDSTRING_H 
#define STDSTRING_H
Salin selepas log masuk

Ini adalah standard termasuk pengawal. Seterusnya, kita perlu memasukkan dua tajuk berikut ke dalam kategori sertakan:

#sertakan
#include
Yang pertama adalah untuk kelas std::string, manakala yang kedua termasuk adalah untuk semua yang berkaitan dengan Node dan V8.

Selepas langkah ini selesai, kami boleh mengisytiharkan kelas kami sendiri:

kelas STDStringWrapper : nod awam::ObjectWrap {
Untuk semua kelas yang kami ingin sertakan dalam pemalam, kami mesti melanjutkan kelas nod::ObjectWrap.

Kini kita boleh mula mentakrifkan sifat peribadi kelas ini:

private: 
  std::string* s_; 
  
  explicit STDStringWrapper(std::string s = ""); 
  ~STDStringWrapper();
Salin selepas log masuk

Selain fungsi pembina dan penghuraian, kita juga perlu menentukan penunjuk untuk std::string. Ini adalah teras teknologi yang boleh digunakan untuk antara muka pangkalan kod C/C dengan Node - kami mentakrifkan penunjuk peribadi untuk kelas C/C dan akan menggunakan penunjuk ini untuk melaksanakan operasi dalam semua kaedah berikutnya.

Kini kami mengisytiharkan sifat statik pembina yang akan menyediakan fungsi untuk kelas yang kami buat dalam V8:

v8 statik::Pembina yang berterusan;
Rakan-rakan yang berminat boleh klik di sini untuk merujuk pelan penerangan templat untuk butiran lanjut.

Sekarang kami juga memerlukan kaedah Baharu, yang akan diberikan kepada pembina yang disebutkan sebelum ini, dan V8 akan memulakan kelas kami:

v8 statik::Handle New(const v8::Arguments& args);
Setiap fungsi yang bertindak pada V8 harus mengikut keperluan berikut: ia akan menerima rujukan kepada objek v8::Arguments dan mengembalikan v8::Handle>v8::Value> - inilah yang kami gunakan pemegang pengekodan C yang kuat lemah menaip JavaScript secara konsisten.

Selepas ini, kita juga perlu memasukkan dua kaedah lain ke dalam prototaip objek:

static v8::Handle add(const v8::Arguments& args); 
static v8::Handle toString(const v8::Arguments& args);
Salin selepas log masuk

Kaedah toString() membolehkan kami memperoleh nilai s_ dan bukannya nilai [Object object] apabila menggunakannya dengan rentetan JavaScript biasa.

Akhir sekali, kami akan memperkenalkan kaedah permulaan (kaedah ini akan dipanggil oleh V8 dan diberikan kepada fungsi pembina) dan matikan pengawal termasuk:

public: 
    static void Init(v8::Handle exports); 
}; 
  
#endif
Salin selepas log masuk

其中exports对象在JavaScript模块中的作用等同于module.exports。

stdstring.cc文件、构造函数与解析函数

现在来创建stdstring.cc文件。我们首先需要include我们的header:

#include "stdstring.h" 
Salin selepas log masuk

下面为constructor定义属性(因为它属于静态函数):

v8::Persistent STDStringWrapper::constructor; 
Salin selepas log masuk

这个为类服务的构造函数将分配s_属性:

STDStringWrapper::STDStringWrapper(std::string s) { 
  s_ = new std::string(s); 
}
Salin selepas log masuk

而解析函数将对其进行delete,从而避免内存溢出:

STDStringWrapper::~STDStringWrapper() { 
  delete s_; 
}
Salin selepas log masuk

再有,大家必须delete掉所有与new一同分配的内容,因为每一次此类情况都有可能造成异常,因此请牢牢记住上述操作或者使用共享指针。

Init方法

该方法将由V8加以调用,旨在对我们的类进行初始化(分配constructor,将我们所有打算在JavaScript当中使用的内容安置在exports对象当中):

void STDStringWrapper::Init(v8::Handle exports) {
首先,我们需要为自己的New方法创建一个函数模板:

v8::Local tpl = v8::FunctionTemplate::New(New);
这有点类似于JavaScipt当中的new Function——它允许我们准备好自己的JavaScript类。

现在我们可以根据实际需要为该函数设定名称了(如果大家漏掉了这一步,那么构造函数将处于匿名状态,即名称为function someName() {}或者function () {}):

tpl->SetClassName(v8::String::NewSymbol("STDString"));
我们利用v8::String::NewSymbol()来创建一个用于属性名称的特殊类型字符串——这能为引擎的运作节约一点点时间。

在此之后,我们需要设定我们的类实例当中包含多少个字段:

tpl->InstanceTemplate()->SetInternalFieldCount(2);
我们拥有两个方法——add()与toString(),因此我们将数量设置为2。现在我们可以将自己的方法添加到函数原型当中了:

tpl->PrototypeTemplate()->Set(v8::String::NewSymbol("add"), v8::FunctionTemplate::New(add)->GetFunction());
tpl->PrototypeTemplate()->Set(v8::String::NewSymbol("toString"), v8::FunctionTemplate::New(toString)->GetFunction());
这部分代码量看起来比较大,但只要认真观察大家就会发现其中的规律:我们利用tpl->PrototypeTemplate()->Set()来添加每一个方法。我们还利用v8::String::NewSymbol()为它们提供名称与FunctionTemplate。

最后,我们可以将该构造函数安置于我们的constructor类属性内的exports对象中:

constructor = v8::Persistent::New(tpl->GetFunction()); 
  exports->Set(v8::String::NewSymbol("STDString"), constructor); 
}
Salin selepas log masuk

New方法

现在我们要做的是定义一个与JavaScript Object.prototype.constructor运作效果相同的方法:

v8::Handle STDStringWrapper::New(const v8::Arguments& args) {
Salin selepas log masuk

我们首先需要为其创建一个范围:

v8::HandleScope scope; 
Salin selepas log masuk

在此之后,我们可以利用args对象的.IsConstructCall()方法来检查该构造函数是否能够利用new关键词加以调用:

if (args.IsConstructCall()) { 
Salin selepas log masuk

如果可以,我们首先如下所示将参数传递至std::string处:

v8::String::Utf8Value str(args[0]->ToString()); 
std::string s(*str);
Salin selepas log masuk

……这样我们就能将它传递到我们封装类的构造函数当中了:

STDStringWrapper* obj = new STDStringWrapper(s);
Salin selepas log masuk

在此之后,我们可以利用之前创建的该对象的.Wrap()方法(继承自node::ObjectWrap)来将它分配给this变量:

obj->Wrap(args.This()); 
Salin selepas log masuk

最后,我们可以返回这个新创建的对象:

return args.This(); 
Salin selepas log masuk

如果该函数无法利用new进行调用,我们也可以直接调用构造函数。接下来,我们要做的是为参数计数设置一个常数:

} else { 
  const int argc = 1; 
Salin selepas log masuk

现在我们需要利用自己的参数创建一个数组:

v8::Local argv[argc] = { args[0] }; 
Salin selepas log masuk

然后将constructor->NewInstance方法的结果传递至scope.Close,这样该对象就能在随后发挥作用(scope.Close基本上允许大家通过将对象处理句柄移动至更高范围的方式对其加以维持——这也是函数的起效方式):

    return scope.Close(constructor->NewInstance(argc, argv)); 
  } 
} 
Salin selepas log masuk

add方法

现在让我们创建add方法,它的作用是允许大家向对象的内部std::string添加内容:

v8::Handle STDStringWrapper::add(const v8::Arguments& args) { 
Salin selepas log masuk

首先,我们需要为我们的函数创建一个范围,并像之前那样把该参数转换到std::string当中:

v8::HandleScope scope; 
  
v8::String::Utf8Value str(args[0]->ToString()); 
std::string s(*str); 
Salin selepas log masuk

现在我们需要对该对象进行拆包。我们之前也进行过这种反向封装操作——这一次我们是要从this变量当中获取指向对象的指针。

STDStringWrapper* obj = ObjectWrap::Unwrap(args.This()); 
Salin selepas log masuk

接着我们可以访问s_属性并使用其.append()方法:

obj->s_->append(s); 
Salin selepas log masuk

最后,我们返回s_属性的当前值(需要再次使用scope.Close):

return scope.Close(v8::String::New(obj->s_->c_str())); 
Salin selepas log masuk

由于v8::String::New()方法只能将char pointer作为值来接受,因此我们需要使用obj->s_->c_str()来加以获取。

这时大家的插件文件夹中还应该创建出一个build目录。

测试

现在我们可以对自己的插件进行测试了。在我们的插件目录中创建一个test.js文件以及必要的编译库(大家可以直接略过.node扩展):

var addon = require('./build/Release/addon'); 
Salin selepas log masuk

下一步,为我们的对象创建一个新实例:

var test = new addon.STDString('test'); 
Salin selepas log masuk

下面再对其进行操作,例如添加或者将其转化为字符串:

test.add('!'); 
console.log('test\'s contents: %s', test); 
Salin selepas log masuk

在运行之后,大家应该在控制台中看到以下执行结果:

结论

我希望大家能在阅读了本教程之后打消顾虑,将创建与测试以C/C++库为基础的定制化Node.js插件视为一项无甚难度的任务。大家可以利用这种技术轻松将几乎任何C/C++库引入Node.js当中。如果大家愿意,还可以根据实际需求为插件添加更多功能。std::string当中提供大量方法,我们可以将它们作为练习素材。

实用链接

感兴趣的朋友可以查看以下链接以获取更多与Node.js插件开发、V8以及C事件循环库相关的资源与详细信息。

• Node.js插件说明文档

• V8说明文档

• libuv (C事件循环库),来自GitHub

英文:http://code.tutsplus.com/tutorials/writing-nodejs-addons--cms-21771

Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan