Aunue tengamos tiempo y experiencia programando en JS, existen ocasiones en que el rendimiento de JavaScript no es suficiente, por lo que se debe depender más de los módulos nativos de Node.js. Sigue leyendo para que te enteres cómo hacer esto.
Bien que les extensions natives ne soient certainement pas un sujet pour les débutants, je recommanderais cet article à tous les développeurs Node.js pour avoir un petit aperçu de leur fonctionnement.
Cas d'utilisation courants des modules Node.js natifs
La connaissance des modules natifs est utile lorsque vous ajoutez une extension native en tant que dépendance, ce que vous auriez pu déjà faire!
Jetez simplement un œil à la liste de certains modules populaires qui utilisent des extensions natives. Vous en utilisez au moins un, ou ai-je tort?
- https://github.com/wadey/node-microtime
- https://github.com/node-inspector
- https://github.com/node-inspector/v8-profiler
- http://www.nodegit.org/
Il y a plusieurs raisons pour lesquelles on pourrait envisager la création de modules Node.js des locuteurs natifs, y compris mais sans s'y limiter:
- Applications critiques pour les performances: Soyons honnêtes, Node.js est idéal pour effectuer des opérations d'entrée et de sortie asynchrones, mais en ce qui concerne le vrai calcul de nombres, ce n'est pas un si bon choix.
- Conexión a las API de nivel inferior, por ejemplo: sistema operativo.
- Pontage des bibliothèques C ou C ++ et Node.js
Quels sont les modules natifs?
Les plugins Node.js sont des objets partagés liés dynamiquement écrits en C ou C ++, qui peuvent être chargés dans Node.js à l'aide de la fonction exiger ()
, et sont utilisés comme s'il s'agissait d'un module Node.js ordinaire.
Cela signifie que (si cela est fait correctement) les bizarreries de C / C ++ peuvent être cachées au consommateur de module. Ce que vous verrez à la place, c'est que votre module est un module Node.js, comme s'il y avait
Node.js s'exécute sur le moteur JavaScript V8, qui est un programme C à lui seul. Nous pouvons écrire du code qui interagit directement avec ce programme C dans votre propre langage, ce qui est formidable car nous pouvons éviter des coûts de sérialisation et de communication coûteux.
Además, en una entrada de Blog anterior que hemos aprendido sobre el costo del colector de basura Node.js. Aunque la recolección de basura se puede evitar por completo si decides administrar la memoria tu mismo (porque C y C ++ no tiene un concepto de GC), creará problemas de memoria mucho más fácilmente.
L'écriture d'extensions natives nécessite la connaissance d'un ou plusieurs des sujets suivants:
- Libuv
- V8
- Internes Node.js
Ils ont tous une excellente documentation. Si vous entrez dans ce champ, je vous recommande de les lire.
Sin más preámbulos, vamos a comenzar con el tema fuerte de este Publier:
Conditions préalables
- Pour le système d'exploitation Linux:
- Utilisez python (je recommande d'utiliser la v2.7, car la v3.xx n'est pas prise en charge)
- Faire.
- Utilisez une chaîne d'outils de compilation C ou C ++ appropriée, telle que GCC
- Pour le système d'exploitation Mac:
- Faites installer Xcode: assurez-vous non seulement de l'installer, mais de le démarrer au moins une fois et d'accepter ses termes et conditions; sinon ça ne marchera pas!
- Pour le système d'exploitation Windows:
- Cours
cmd.exe
en tant qu'administrateur et entrez la commandenpm install --global --production windows-build-tools
, qui installera tout pour vous. - Une autre option consiste à installer Visual Studio: (il a tous les outils de génération C / C ++ préconfigurés)
- Ou utilisez le sous-système Linux fourni par la dernière version de Windows. Avec cela, suivez les instructions LINUX ci-dessus.
- Cours
Création de notre extension native Node.js
Nous allons créer notre premier fichier pour l'extension native. Nous pouvons utiliser l'extension .DC ce qui signifie que c'est C avec des classes, ou l'extension .cpp que es la predeterminada para C ++. La Guía de estilo de Google recomienda .DC, donc pour ce tutoriel, je vais m'en tenir à lui.
À ce stade, nous allons voir le fichier complet et ensuite l'expliquer ligne par ligne.
#include const int maxValue = 10; int numberOfCalls = 0; void WhoAmI (const v8 :: FunctionCallbackInfo & args) {v8 :: Isolate * isolate = args.GetIsolate (); message automatique = v8 :: String :: NewFromUtf8 (isoler, "Je suis Node Hero!"); args.GetReturnValue (). Set (message); } incrément void (const v8 :: FunctionCallbackInfo & args) {v8 :: Isolate * isolate = args.GetIsolate (); if (! args [0] -> IsNumber ()) {isolate-> ThrowException (v8 :: Exception :: TypeError (v8 :: String :: NewFromUtf8 (isolate, "L'argument doit être un nombre"))); revenir; } double argsValue = args [0] -> NumberValue (); if (numberOfCalls + argsValue> maxValue) {isolate-> ThrowException (v8 :: Exception :: Error (v8 :: String :: NewFromUtf8 (isolate, "Le compteur a traversé le toit!"))); revenir; } numberOfCalls + = argsValue; auto currentNumberOfCalls = v8 :: Number :: New (isoler, static_cast (numberOfCalls)); args.GetReturnValue (). Set (currentNumberOfCalls); } void Initialize (v8 :: Local exportations) {NODE_SET_METHOD (exportations, "whoami", WhoAmI); NODE_SET_METHOD (exportations, "incrémentation", incrément); } NODE_MODULE (nom_module, initialiser)
Maintenant, nous allons voir le fichier ligne par ligne
#include
le inclure
en C ++ c'est comme exiger ()
en JavaScript. Il extraira tout du fichier donné, mais au lieu de le lier directement à la source, en C ++, nous avons le concept de fichiers d'en-tête.
Podemos declarar la interfaz exacta en los archivos de encabezado sin implementación y luego podemos incluir las implementaciones por su archivo de encabezado. El enlazador C ++ se encargará de vincular estos dos juntos. Piensa en ello como un archivo de documentación que describe tu Contenu, que puede ser reutilizado desde su código.
void WhoAmI(const v8::FunctionCallbackInfo<v8::value>& args) { v8::Isolate* isolate = args.GetIsolate(); auto message = v8::String::NewFromUtf8(isolate, "Yo soy Node Hero!"); args.GetReturnValue().Set(message); } [php] Debido a que esta será una extensión nativa, el espacio de nombres v8 está disponible para su uso. Ten en cuenta la notación <strong>v8 ::</strong>, que se utiliza para acceder a la interfaz del v8. Si no desea incluir <strong>v8 ::</strong> antes de usar cualquiera de los tipos provistos por el v8, puede agregarlo usando <strong>using</strong> <strong>v8;</strong> a la parte superior del archivo. Luego, puede omitir todos los especificadores <strong>v8 ::</strong> de espacio de nombres de sus tipos, pero esto puede introducir colisiones de nombres en el código, así que ten cuidado al usarlos. Para ser 100% claros, usaré la notación <strong>v8 ::</strong> para todos los tipos de v8 en el código mostrado. En nuestro código de ejemplo, tenemos acceso a los argumentos con los que se llamó a la función (desde JavaScript), a través del objeto <strong>args</strong> que también nos proporciona toda la información relacionada con la llamada. Con <strong>v8::Isolate*</strong> estamos obteniendo acceso al alcance de JavaScript actual para nuestra función. Los ámbitos funcionan igual que en JavaScript: podemos asignar variables y vincularlas a la vida útil de ese código específico. No tenemos que preocuparnos por des-asignar estos trozos de memoria, porque los asignamos como si estuviéramos en JavaScript, y el recolector de basura se encargará de ellos automáticamente. [php] function () { var a = 1; } // envergadura
Passant par args.GetReturnValue () accedemos al valor de retorno de nuestra función. Podemos configurarlo para lo que queramos siempre que être desde el espacio v8 :: des noms.
C ++ a des types intégrés pour stocker des entiers et des chaînes, mais JavaScript ne comprend que ses propres types d'objet v8 ::. Tant que nous sommes dans le domaine du monde C ++, nous pouvons utiliser ceux qui sont intégrés dans C ++, mais lorsque nous traitons des objets JavaScript et de l'interopérabilité avec le code JavaScript, nous devons transformer les types C ++ en à part qu'ils se comprennent. par le contexte JavaScript. Ce sont les types qui sont exposés dans v8 :: namespace en tant que v8 :: Stringo v8 :: Object.
void WhoAmI (const v8 :: FunctionCallbackInfo & args) {v8 :: Isolate * isolate = args.GetIsolate (); message automatique = v8 :: String :: NewFromUtf8 (isoler, "Je suis Node Hero!"); args.GetReturnValue (). Set (message); }
Examinons la deuxième méthode de notre fichier qui incrémente un compteur d'un argument fourni jusqu'à une limite supérieure de 10.
Cette fonction accepte également un paramètre JavaScript. Lorsque vous acceptez des paramètres JavaScript, vous devez être prudent car ce sont des objets mal écrits. (Vous êtes probablement déjà habitué à cela en JavaScript.)
Le tableau Array contient v8 :: Objets, por lo que son todos objetos de JavaScript, pero ten cuidado con estos, porque en este contexto nunca podemos estar seguros de lo que pueden contener. Tenemos que verificar explícitamente los tipos de estos objetos. Afortunadamente, hay métodos auxiliares que se agregan a estas clases para determinar su tipo antes de la conversion de tipos.
Pour maintenir la compatibilité avec le code JavaScript existant, nous devons lancer une erreur si le type d'arguments est incorrect. Pour lancer une erreur de type, nous devons créer un objet d'erreur avec le constructeur
v8 :: Exception :: TypeError (). Le bloc suivant lancera un Erreur-type si le premier argument n'est pas un nombre.
if (! args [0] -> IsNumber ()) {isolate-> ThrowException (v8 :: Exception :: TypeError (v8 :: String :: NewFromUtf8 (isolate, "L'argument doit être un nombre"))); revenir; }
En JavaScript, cet extrait ressemblerait à ceci:
If (typeof arguments [0]! == 'number') {throw new TypeError ('L'argument doit être un nombre')}