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.
Während native Erweiterungen definitiv kein Anfängerthema sind, würde ich diesen Artikel allen Node.js-Entwicklern empfehlen, um einen kleinen Einblick in ihre Funktionsweise zu erhalten.
Häufige Anwendungsfälle für native Node.js-Module
Kenntnisse über native Module sind hilfreich, wenn Sie eine native Erweiterung als Abhängigkeit hinzufügen, was Sie bereits hätten tun können!
Schauen Sie sich einfach die Liste einiger beliebter Module an, die native Erweiterungen verwenden. Sie verwenden mindestens einen von ihnen, oder irre ich mich?
- https://github.com/wadey/node-microtime
- https://github.com/node-inspector
- https://github.com/node-inspector/v8-profiler
- http://www.nodegit.org/
Es gibt einige Gründe, warum man die Modulerstellung in Betracht ziehen könnte Node.js Muttersprachler, einschließlich, aber nicht beschränkt auf:
- Leistungskritische Anwendungen: Seien wir ehrlich, Node.js eignet sich hervorragend für asynchrone Eingabe- und Ausgabeoperationen, aber wenn es um die Berechnung echter Zahlen geht, ist es keine so gute Wahl.
- Conexión a las API de nivel inferior, por ejemplo: sistema operativo.
- Überbrückung der C- oder C ++ - Bibliotheken und Node.js.
Was sind die nativen Module?
Node.js-Plugins sind dynamisch verknüpfte gemeinsam genutzte Objekte, die in C oder C ++ geschrieben wurden und mit der Funktion in Node.js geladen werden können benötigen ()
und werden so verwendet, als wären sie ein gewöhnliches Node.js-Modul.
Dies bedeutet, dass (wenn es richtig gemacht wird) die Macken von C / C ++ vor dem Modulkonsumenten verborgen werden können. Stattdessen sehen Sie, dass Ihr Modul ein Node.js-Modul ist, als ob es eines gäbe
Node.js läuft auf der V8-JavaScript-Engine, die ein eigenständiges C-Programm ist. Wir können Code schreiben, der direkt mit diesem C-Programm in Ihrer eigenen Sprache interagiert. Dies ist großartig, da wir viele teure Serialisierungs- und Kommunikationskosten vermeiden können.
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.
Das Schreiben nativer Erweiterungen erfordert Kenntnisse über eines oder mehrere der folgenden Themen:
- Libuv
- V8
- Node.js Interna
Sie alle haben eine ausgezeichnete Dokumentation. Wenn Sie dieses Feld eingeben, empfehle ich Ihnen, sie zu lesen.
Sin más preámbulos, vamos a comenzar con el tema fuerte de este Post:
Voraussetzungen
- Für Linux-Betriebssystem:
- Verwenden Sie Python (ich empfehle die Verwendung von v2.7, da v3.xx nicht unterstützt wird)
- Machen.
- Verwenden Sie eine geeignete C- oder C ++ - Kompilierungs-Toolchain, z. B. GCC
- Für Mac-Betriebssystem:
- Lassen Sie Xcode installieren: Stellen Sie sicher, dass Sie es nicht nur installieren, sondern mindestens einmal starten und die allgemeinen Geschäftsbedingungen akzeptieren. sonst klappt es nicht!
- Für Windows-Betriebssystem:
- Lauf
cmd.exe
als Administrator und geben Sie den Befehl einnpm install --global --production windows-build-tools
, die alles für Sie installieren. - Eine weitere Option ist die Installation von Visual Studio: (Alle C / C ++ - Build-Tools sind vorkonfiguriert.)
- Oder verwenden Sie das Linux-Subsystem, das vom neuesten Windows-Build bereitgestellt wird. Befolgen Sie dabei die obigen LINUX-Anweisungen.
- Lauf
Erstellen unserer nativen Node.js-Erweiterung
Wir werden unsere erste Datei für die native Erweiterung erstellen. Wir können die Erweiterung verwenden .DC was bedeutet, es ist C mit Klassen oder die Erweiterung .cpp que es la predeterminada para C ++. La Guía de estilo de Google recomienda .DCAlso, für dieses Tutorial bleibe ich dabei.
An diesem Punkt sehen wir die vollständige Datei und erklären sie dann Zeile für Zeile.
#include const int maxValue = 10; int numberOfCalls = 0; void WhoAmI (const v8 :: FunctionCallbackInfo & args) {v8 :: Isolate * isolate = args.GetIsolate (); auto message = v8 :: String :: NewFromUtf8 (isolieren Sie "Ich bin Node Hero!"); args.GetReturnValue (). Set (message); } void Increment (const v8 :: FunctionCallbackInfo & args) {v8 :: Isolate * isolate = args.GetIsolate (); if (! args [0] -> IsNumber ()) {isolate-> ThrowException (v8 :: Exception :: TypeError (v8 :: String :: NewFromUtf8 (isolate, "Das Argument muss eine Zahl sein")); Rückkehr; } double argsValue = args [0] -> NumberValue (); if (numberOfCalls + argsValue> maxValue) {isolate-> ThrowException (v8 :: Exception :: Error (v8 :: String :: NewFromUtf8 (isolate, "Der Zähler ging durch das Dach!")); Rückkehr; } numberOfCalls + = argsValue; auto currentNumberOfCalls = v8 :: Number :: New (isolate, static_cast (numberOfCalls)); args.GetReturnValue (). Set (currentNumberOfCalls); } void Initialize (v8 :: Local Exporte) {NODE_SET_METHOD (Exporte, "whoami", WhoAmI); NODE_SET_METHOD (Exporte, "Inkrement", Inkrement); } NODE_MODULE (Modulname, Initialisieren)
Jetzt werden wir die Datei Zeile für Zeile sehen
#include
Das einschließen
in C ++ ist es wie benötigen ()
in JavaScript. Es wird alles aus der angegebenen Datei extrahiert, aber anstatt es direkt mit der Quelle zu verknüpfen, haben wir in C ++ das Konzept von Header-Dateien.
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 Inhalt, 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
Über args.GetReturnValue () accedemos al valor de retorno de nuestra función. Podemos configurarlo para lo que queramos siempre que Sein desde el espacio v8 :: von Namen.
C ++ verfügt über integrierte Typen zum Speichern von Ganzzahlen und Zeichenfolgen, JavaScript versteht jedoch nur seine eigenen v8 :: -Objekttypen. Solange wir uns im Bereich der C ++ - Welt befinden, können wir diejenigen verwenden, die in C ++ integriert sind. Wenn wir uns jedoch mit JavaScript-Objekten und der Interoperabilität mit JavaScript-Code befassen, müssen wir die C ++ - Typen in transformieren anders als sie sich verstehen. durch den JavaScript-Kontext. Dies sind die Typen, die im v8 :: -Namensraum als v8 :: Stringo v8 :: Object verfügbar gemacht werden.
void WhoAmI (const v8 :: FunctionCallbackInfo & args) {v8 :: Isolate * isolate = args.GetIsolate (); auto message = v8 :: String :: NewFromUtf8 (isolieren Sie "Ich bin Node Hero!"); args.GetReturnValue (). Set (message); }}
Schauen wir uns die zweite Methode in unserer Datei an, die einen Zähler um ein angegebenes Argument bis zu einer Obergrenze von 10 erhöht.
Diese Funktion akzeptiert auch einen JavaScript-Parameter. Wenn Sie JavaScript-Parameter akzeptieren, müssen Sie vorsichtig sein, da es sich um lose geschriebene Objekte handelt. (In JavaScript sind Sie wahrscheinlich inzwischen daran gewöhnt.)
Array Array enthält v8 :: Objekte, 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 Umwandlung de tipos.
Um die Kompatibilität mit vorhandenem JavaScript-Code zu gewährleisten, sollten wir einen Fehler auslösen, wenn die Art der Argumente falsch ist. Um einen Typfehler auszulösen, müssen wir mit dem Konstruktor ein Fehlerobjekt erstellen
v8 :: Exception :: TypeError (). Der nächste Block startet a TypeError wenn das erste Argument keine Zahl ist.
if (! args [0] -> IsNumber ()) {isolate-> ThrowException (v8 :: Exception :: TypeError (v8 :: String :: NewFromUtf8 (isolate, "Das Argument muss eine Zahl sein")); Rückkehr; }}
In JavaScript würde dieses Snippet folgendermaßen aussehen:
If (Typ der Argumente [0]! == 'Zahl') {neuen TypeError auslösen ('Das Argument muss eine Zahl sein')}