Maison >interface Web >js tutoriel >Un moyen simple d'écrire des extensions pour d'autres programmes à l'aide de Node.js_node.js

Un moyen simple d'écrire des extensions pour d'autres programmes à l'aide de Node.js_node.js

WBOY
WBOYoriginal
2016-05-16 15:53:261226parcourir

Prêt à commencer

Nous utilisons d'abord la structure de répertoires suivante pour créer un dossier node-notify.

Copier le code Le code est le suivant :

.
|-- build/                                       # C'est ici que notre extension est construite.
|-- démo/
| `-- demo.js # Ceci est un script de démonstration Node.js pour tester notre extension.
|--src/
| `-- node_gtknotify.cpp # C'est ici que nous effectuons le mappage de C vers Javascript.
`-- wscript                                # Ceci est notre configuration de build utilisée par node-waf

Ce bel arbre est généré à l'aide d'un arbre générique.

Maintenant, laissez-moi créer le script de test demo.js et décider dès le départ à quoi devrait ressembler l'API de notre extension :

// This loads our extension on the notify variable.
// It will only load a constructor function, notify.notification().
var notify = require("../build/default/gtknotify.node"); // path to our extension
 
var notification = new notify.notification();
notification.title = "Notification title";
notification.icon = "emblem-default"; // see /usr/share/icons/gnome/16x16
notification.send("Notification message");

Écrire notre extension Node.js
Méthode d'initialisation

Afin de créer une extension Node.js, nous devons écrire une classe C qui hérite de node::ObjectWrap. ObjectWrap implémente des méthodes publiques qui nous permettent d'interagir plus facilement avec Javascript

Écrivons d’abord le cadre de base du cours :

#include <v8.h> // v8 is the Javascript engine used by QNode
#include <node.h>
// We will need the following libraries for our GTK+ notification
#include <string>
#include <gtkmm.h>
#include <libnotifymm.h>
 
using namespace v8;
 
class Gtknotify : node::ObjectWrap {
 private:
 public:
  Gtknotify() {}
  ~Gtknotify() {}
  static void Init(Handle<Object> target) {
   // This is what Node will call when we load the extension through require(), see boilerplate code below.
  }
};
 
/*
 * WARNING: Boilerplate code ahead.
 *
 * See https://www.cloudkick.com/blog/2010/aug/23/writing-nodejs-native-extensions/ & http://www.freebsd.org/cgi/man.cgi&#63;query=dlsym
 * 
 * Thats it for actual interfacing with v8, finally we need to let Node.js know how to dynamically load our code.
 * Because a Node.js extension can be loaded at runtime from a shared object, we need a symbol that the dlsym function can find,
 * so we do the following: 
 */
 
v8::Persistent<FunctionTemplate> Gtknotify::persistent_function_template;
extern "C" { // Cause of name mangling in C++, we use extern C here
 static void init(Handle<Object> target) {
  Gtknotify::Init(target);
 }
 // @see http://github.com/ry/node/blob/v0.2.0/src/node.h#L101
 NODE_MODULE(gtknotify, init);
}

Maintenant, nous devons écrire le code suivant dans notre méthode Init() :

Déclarez le constructeur et liez-le à notre variable cible. var n = require("notification"); liera notification() à n:n.notification().

// Wrap our C++ New() method so that it's accessible from Javascript
  // This will be called by the new operator in Javascript, for example: new notification();
  v8::Local<FunctionTemplate> local_function_template = v8::FunctionTemplate::New(New);
   
  // Make it persistent and assign it to persistent_function_template which is a static attribute of our class.
  Gtknotify::persistent_function_template = v8::Persistent<FunctionTemplate>::New(local_function_template);
   
  // Each JavaScript object keeps a reference to the C++ object for which it is a wrapper with an internal field.
  Gtknotify::persistent_function_template->InstanceTemplate()->SetInternalFieldCount(1); // 1 since a constructor function only references 1 object
  // Set a "class" name for objects created with our constructor
  Gtknotify::persistent_function_template->SetClassName(v8::String::NewSymbol("Notification"));
   
  // Set the "notification" property of our target variable and assign it to our constructor function
  target->Set(String::NewSymbol("notification"), Gtknotify::persistent_function_template->GetFunction());

Attributs de déclaration : n.title et n.icon.

  // Set property accessors
  // SetAccessor arguments: Javascript property name, C++ method that will act as the getter, C++ method that will act as the setter
  Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("title"), GetTitle, SetTitle);
  Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("icon"), GetIcon, SetIcon);
  // For instance, n.title = "foo" will now call SetTitle("foo"), n.title will now call GetTitle()

Déclarer la méthode prototype : n.send()

  // This is a Node macro to help bind C++ methods to Javascript methods (see https://github.com/joyent/node/blob/v0.2.0/src/node.h#L34)
  // Arguments: our constructor function, Javascript method name, C++ method name
  NODE_SET_PROTOTYPE_METHOD(Gtknotify::persistent_function_template, "send", Send);

Maintenant, notre méthode Init() devrait ressembler à ceci :

// Our constructor
static v8::Persistent<FunctionTemplate> persistent_function_template;
 
static void Init(Handle<Object> target) {
 v8::HandleScope scope; // used by v8 for garbage collection
 
 // Our constructor
 v8::Local<FunctionTemplate> local_function_template = v8::FunctionTemplate::New(New);
 Gtknotify::persistent_function_template = v8::Persistent<FunctionTemplate>::New(local_function_template);
 Gtknotify::persistent_function_template->InstanceTemplate()->SetInternalFieldCount(1); // 1 since this is a constructor function
 Gtknotify::persistent_function_template->SetClassName(v8::String::NewSymbol("Notification"));
 
 // Our getters and setters
 Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("title"), GetTitle, SetTitle);
 Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("icon"), GetIcon, SetIcon);
 
 // Our methods
 NODE_SET_PROTOTYPE_METHOD(Gtknotify::persistent_function_template, "send", Send);
 
 // Binding our constructor function to the target variable
 target->Set(String::NewSymbol("notification"), Gtknotify::persistent_function_template->GetFunction());
}

Il ne reste plus qu'à écrire la méthode C que nous utilisons dans la méthode Init : New, GetTitle, SetTitle, GetIcon, SetIcon, Send

Méthode constructeur : New()

La méthode

New() crée une nouvelle instance de notre classe personnalisée (un objet Gtknotify), définit certaines valeurs initiales, puis renvoie le gestionnaire JavaScript pour cet objet. C'est le comportement attendu de JavaScript appelant un constructeur à l'aide de l'opérateur new.

 
std::string title;
std::string icon;
 
// new notification()
static Handle<Value> New(const Arguments& args) {
 HandleScope scope;
 Gtknotify* gtknotify_instance = new Gtknotify();
 // Set some default values
 gtknotify_instance->title = "Node.js";
 gtknotify_instance->icon = "terminal";
 
 // Wrap our C++ object as a Javascript object
 gtknotify_instance->Wrap(args.This());
 
 return args.This();
}
getters 和 setters: GetTitle(), SetTitle(), GetIcon(), SetIcon()

Ce qui suit est principalement du code passe-partout qui se résume à la conversion de valeurs entre C et JavaScript (v8).

// this.title
static v8::Handle<Value> GetTitle(v8::Local<v8::String> property, const v8::AccessorInfo& info) {
 // Extract the C++ request object from the JavaScript wrapper.
 Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap<Gtknotify>(info.Holder());
 return v8::String::New(gtknotify_instance->title.c_str());
}
// this.title=
static void SetTitle(Local<String> property, Local<Value> value, const AccessorInfo& info) {
 Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap<Gtknotify>(info.Holder());
 v8::String::Utf8Value v8str(value);
 gtknotify_instance->title = *v8str;
}
// this.icon
static v8::Handle<Value> GetIcon(v8::Local<v8::String> property, const v8::AccessorInfo& info) {
 // Extract the C++ request object from the JavaScript wrapper.
 Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap<Gtknotify>(info.Holder());
 return v8::String::New(gtknotify_instance->icon.c_str());
}
// this.icon=
static void SetIcon(Local<String> property, Local<Value> value, const AccessorInfo& info) {
 Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap<Gtknotify>(info.Holder());
 v8::String::Utf8Value v8str(value);
 gtknotify_instance->icon = *v8str;
}

Méthode prototype : Envoyer()

Nous extrayons d'abord la référence this de l'objet C, puis utilisons les propriétés de l'objet pour créer la notification et l'afficher.

// this.send()
static v8::Handle<Value> Send(const Arguments& args) {
 v8::HandleScope scope;
 // Extract C++ object reference from "this"
 Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap<Gtknotify>(args.This());
 
 // Convert first argument to V8 String
 v8::String::Utf8Value v8str(args[0]);
 
 // For more info on the Notify library: http://library.gnome.org/devel/libnotify/0.7/NotifyNotification.html
 Notify::init("Basic");
 // Arguments: title, content, icon
 Notify::Notification n(gtknotify_instance->title.c_str(), *v8str, gtknotify_instance->icon.c_str()); // *v8str points to the C string it wraps
 // Display the notification
 n.show();
 // Return value
 return v8::Boolean::New(true);
}

Extension de compilation

node-waf est un outil de construction utilisé pour compiler les extensions Node, qui est le package de base de waf. Le processus de construction est configurable via un fichier appelé wscript.

def set_options(opt):
 opt.tool_options("compiler_cxx")
 
def configure(conf):
 conf.check_tool("compiler_cxx")
 conf.check_tool("node_addon")
 # This will tell the compiler to link our extension with the gtkmm and libnotifymm libraries.
 conf.check_cfg(package='gtkmm-2.4', args='--cflags --libs', uselib_store='LIBGTKMM')
 conf.check_cfg(package='libnotifymm-1.0', args='--cflags --libs', uselib_store='LIBNOTIFYMM')
 
def build(bld):
 obj = bld.new_task_gen("cxx", "shlib", "node_addon")
 obj.cxxflags = ["-g", "-D_FILE_OFFSET_BITS=64", "-D_LARGEFILE_SOURCE", "-Wall"]
 # This is the name of our extension.
 obj.target = "gtknotify"
 obj.source = "src/node_gtknotify.cpp"
 obj.uselib = ['LIBGTKMM', 'LIBNOTIFYMM']

Maintenant que nous sommes prêts à commencer la construction, exécutez la commande suivante dans le répertoire de niveau supérieur :


node-waf configurer && node-waf build

Si tout se passe bien, nous obtiendrons l'extension compilée, située à l'adresse : ./build/default/gtknotify.node, essayons :

$ node
> var notif = require('./build/default/gtknotify.node');
> n = new notif.notification();
{ icon: 'terminal', title: 'Node.js' }
> n.send("Hello World!");
true

Le code ci-dessus affichera un message de notification dans le coin supérieur droit de votre écran.

Emballé dans un package npm

C'est très cool, mais comment partager les résultats de vos efforts avec la communauté Node. C'est l'objectif principal de npm : faciliter son extension et sa distribution.

Emballer une extension avec npm est très simple. Tout ce que vous avez à faire est de créer un fichier package.json dans votre répertoire de niveau supérieur qui contient les informations de votre extension :

{
 // 扩展的名称 (不要在名称中包含node 或者 js, 这是隐式关键字).
 // 这是通过require() 导入扩展的名称.
 
 "name" : "notify",
 
 // Version should be http://semver.org/ compliant
 
 "version" : "v0.1.0"
 
 // 这些脚本将在调用npm安装和npm卸载的时候运行.
 
 , "scripts" : {
   "preinstall" : "node-waf configure && node-waf build"
   , "preuninstall" : "rm -rf build/*"
  }
 
 // 这是构建我们扩展的相对路径.
 
 , "main" : "build/default/gtknotify.node"
 
 // 以下是可选字段:
 
 , "description" : "Description of the extension...."
 , "homepage" : "https://github.com/olalonde/node-notify"
 , "author" : {
   "name" : "Olivier Lalonde"
   , "email" : "olalonde@gmail.com"
   , "url" : "http://www.syskall.com/"
  }
 , "repository" : {
   "type" : "git"
   , "url" : "https://github.com/olalonde/node-notify.git"
  }
}

Pour plus de détails sur le format package.json, la documentation peut être obtenue via npm help json Notez que la plupart des champs sont facultatifs.


Vous pouvez maintenant installer votre nouveau package npm en exécutant npm install dans votre répertoire de niveau supérieur. Si tout se passe bien, vous devriez pouvoir simplement charger votre extension var notify = require('your package name');. npm link. Avec cette commande, vous pouvez créer un lien vers votre répertoire de développement, vous n'avez donc pas besoin d'installer/désinstaller à chaque fois que votre code change

.

Supposons que vous écriviez une extension sympa, vous souhaiterez peut-être la publier en ligne dans un référentiel npm central. Vous devez d'abord créer un compte :

.


$ npm adduser

下一步, 回到你的根目录编码并且运行:

 
$ npm publish

就是这样, 你的包现在已经可以被任何人通过npm install 你的包名命令来安装了.

 

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn