721 Stimmen

Was ist dieses JavaScript "require"?

Ich versuche, JavaScript zum Lesen/Schreiben in eine PostgreSQL-Datenbank zu bekommen. Ich fand dies Projekt auf GitHub. Ich konnte den folgenden Beispielcode in Node zum Laufen bringen.

var pg = require('pg'); //native libpq bindings = `var pg = require('pg').native`
var conString = "tcp://postgres:1234@localhost/postgres";

var client = new pg.Client(conString);
client.connect();

//queries are queued and executed one after another once the connection becomes available
client.query("CREATE TEMP TABLE beatles(name varchar(10), height integer, birthday timestamptz)");
client.query("INSERT INTO beatles(name, height, birthday) values($1, $2, $3)", ['Ringo', 67, new Date(1945, 11, 2)]);
client.query("INSERT INTO beatles(name, height, birthday) values($1, $2, $3)", ['John', 68, new Date(1944, 10, 13)]);

//queries can be executed either via text/parameter values passed as individual arguments
//or by passing an options object containing text, (optional) parameter values, and (optional) query name
client.query({
  name: 'insert beatle',
  text: "INSERT INTO beatles(name, height, birthday) values($1, $2, $3)",
  values: ['George', 70, new Date(1946, 02, 14)]
});

//subsequent queries with the same name will be executed without re-parsing the query plan by postgres
client.query({
  name: 'insert beatle',
  values: ['Paul', 63, new Date(1945, 04, 03)]
});
var query = client.query("SELECT * FROM beatles WHERE name = $1", ['John']);

//can stream row results back 1 at a time
query.on('row', function(row) {
  console.log(row);
  console.log("Beatle name: %s", row.name); //Beatle name: John
  console.log("Beatle birth year: %d", row.birthday.getYear()); //dates are returned as javascript dates
  console.log("Beatle height: %d' %d\"", Math.floor(row.height/12), row.height%12); //integers are returned as javascript ints
});

//fired after last row is emitted
query.on('end', function() { 
  client.end();
});

Als Nächstes habe ich versucht, es auf einer Webseite laufen zu lassen, aber es schien nichts zu passieren. Ich habe in der JavaScript-Konsole nachgesehen, und es heißt nur "require not defined".

Was ist also dieses "erfordern"? Warum funktioniert es in Node, aber nicht in einer Webseite?

Außerdem musste ich, bevor ich es in Node zum Laufen brachte, Folgendes tun npm install pg . Was hat es damit auf sich? Ich habe in dem Verzeichnis nachgesehen und keine Datei pg gefunden. Wo hat es sie abgelegt, und wie findet JavaScript sie?

1191voto

Joseph Punkte 113912

Was ist also diese "Anforderung"?

require() ist nicht Teil der Standard-JavaScript-API. Aber in Node.js ist es eine eingebaute Funktion mit einem besonderen Zweck: zum Laden von Modulen .

Module sind eine Möglichkeit, eine Anwendung in separate Dateien aufzuteilen, anstatt die gesamte Anwendung in einer Datei zu haben. Dieses Konzept gibt es auch in anderen Sprachen mit geringfügigen Unterschieden in Syntax und Verhalten, wie z.B. in C include , Python's import , und so weiter.

Ein großer Unterschied zwischen Node.js-Modulen und Browser-JavaScript besteht darin, wie auf den Code des einen Skripts vom Code eines anderen Skripts aus zugegriffen wird.

  • In Browser-JavaScript werden die Skripte über die <script> Element. Wenn sie ausgeführt werden, haben sie alle direkten Zugriff auf den globalen Bereich, einen "gemeinsamen Raum" für alle Skripte. Jedes Skript kann alles im globalen Bereich frei definieren/ändern/entfernen/aufrufen.

  • In Node.js hat jedes Modul seinen eigenen Geltungsbereich. Ein Modul kann nicht direkt auf Dinge zugreifen, die in einem anderen Modul definiert sind, es sei denn, es beschließt, sie freizugeben. Um Dinge aus einem Modul freizugeben, müssen sie zu exports o module.exports . Damit ein Modul auf die Daten eines anderen Moduls zugreifen kann exports o module.exports , Sie muss require() .

In Ihrem Code, var pg = require('pg'); lädt die pg Modul, ein PostgreSQL-Client für Node.js. Dies ermöglicht Ihrem Code den Zugriff auf die Funktionalität der APIs des PostgreSQL-Clients über die pg variabel.

Warum funktioniert es in Node, aber nicht in einer Webseite?

require() , module.exports y exports sind APIs eines Modulsystems, das spezifisch für Node.js ist. Browser implementieren dieses Modulsystem nicht.

Außerdem musste ich, bevor ich es in Node zum Laufen brachte, Folgendes tun npm install pg . Was hat das zu bedeuten?

NPM ist ein Paketverwaltungsdienst, der veröffentlichte JavaScript-Module bereitstellt. npm install ist ein Befehl, mit dem Sie Pakete aus ihrem Repository herunterladen können.

Wo hat es sie abgelegt, und wie findet Javascript sie?

Der npm-Cli legt alle heruntergeladenen Module in einem node_modules Verzeichnis, in dem Sie npm install . Node.js hat eine sehr detaillierte Dokumentation über wie Module andere Module finden was die Suche nach einem node_modules Verzeichnis.

126voto

Timothy Meade Punkte 2416

Beginnen wir also mit der Unterscheidung zwischen Javascript in einem Webbrowser und Javascript auf einem Server (CommonJS und Node).

Javascript ist eine Sprache, die traditionell auf einen Webbrowser mit einem begrenzten globalen Kontext beschränkt ist, der hauptsächlich durch das sogenannte Document Object Model (DOM) Level 0 (die Netscape Navigator Javascript API) definiert ist.

Serverseitiges Javascript hebt diese Einschränkung auf und ermöglicht es Javascript, verschiedene Teile des nativen Codes (wie die Postgres-Bibliothek) aufzurufen und Sockets zu öffnen.

Jetzt require() ist ein spezieller Funktionsaufruf, der als Teil der CommonJS-Spezifikation definiert ist. In Node löst er Bibliotheken und Module im Node-Suchpfad auf, der jetzt üblicherweise definiert ist als node_modules im gleichen Verzeichnis (oder im Verzeichnis der aufgerufenen Javascript-Datei) oder im systemweiten Suchpfad.

Um den Rest Ihrer Frage zu beantworten, müssen wir einen Proxy zwischen dem im Browser laufenden Code und dem Datenbankserver verwenden.

Da wir über Node sprechen und Sie bereits damit vertraut sind, wie man eine Abfrage von dort aus ausführt, wäre es sinnvoll, Node als Proxy zu verwenden.

Als einfaches Beispiel werden wir eine URL erstellen, die einige Fakten über einen Beatle als JSON zurückgibt, wenn ein Name angegeben wird.

/* your connection code */

var express = require('express');
var app = express.createServer();
app.get('/beatles/:name', function(req, res) {
    var name = req.params.name || '';
    name = name.replace(/[^a-zA_Z]/, '');
    if (!name.length) {
        res.send({});
    } else {
        var query = client.query('SELECT * FROM BEATLES WHERE name =\''+name+'\' LIMIT 1');
        var data = {};
        query.on('row', function(row) {
            data = row;
            res.send(data);
        });
    };
});
app.listen(80, '127.0.0.1');

42voto

Sam Redway Punkte 6864

Mir ist aufgefallen, dass die anderen Antworten zwar erklären, was require ist und dass es zum Laden von Modulen in Node verwendet wird, aber keine vollständige Antwort darauf geben, wie man Node-Module lädt, wenn man im Browser arbeitet.

Das ist ganz einfach. Installieren Sie Ihr Modul mit npm, wie Sie es beschreiben. Das Modul selbst befindet sich dann in einem Ordner, der normalerweise node_modules heißt.

Der einfachste Weg, es in Ihre Anwendung zu laden, besteht darin, es in Ihrer HTML-Datei mit einem Skript-Tag zu referenzieren, das auf dieses Verzeichnis verweist, d.h. wenn sich Ihr node_modules-Verzeichnis im Stammverzeichnis des Projekts auf derselben Ebene wie Ihre index.html befindet, würden Sie dies in Ihre index.html schreiben:

<script src="node_modules/ng"></script>

Das gesamte Skript wird nun in die Seite geladen, so dass Sie direkt auf seine Variablen und Methoden zugreifen können.

Es gibt andere Ansätze, die in größeren Projekten häufiger verwendet werden, wie z.B. ein Modullader wie require.js . Ich habe Require selbst noch nicht verwendet, aber ich glaube, dass es von vielen als die beste Lösung angesehen wird.

37voto

Stefan Steiger Punkte 72861

Nekromantie.
IMHO lassen die vorhandenen Antworten viel zu wünschen übrig.

Am Anfang ist das sehr verwirrend.
Sie haben eine (nirgends definierte) Funktion "require", die dazu dient, Module zu erhalten.
Und in diesen (CommonJS-)Modulen können Sie require, exports and module ,
OHNE DASS SIE JEMALS DEFINIERT WURDEN.
Nicht, dass es neu wäre, dass man undefinierte Variablen in JS verwenden könnte, aber man könnte keine undefinierte Funktion verwenden.
Es sieht also zunächst ein wenig nach Zauberei aus.
Aber alle Magie basiert auf Täuschung.

Wenn man etwas tiefer gräbt, stellt sich heraus, dass es eigentlich ganz einfach ist:
Require ist einfach eine (nicht standardisierte) Funktion auf globaler Ebene definiert .
(globaler Bereich = Fenster-Objekt im Browser, globales Objekt in NodeJS ).
Beachten Sie, dass die "require-Funktion" standardmäßig nur in NodeJS und nicht im Browser implementiert ist.
Um die Verwirrung noch weiter zu vergrößern, gibt es für den Browser RequireJS die, obwohl der Name die Zeichen "require" enthält, RequireJS absolut NICHT require/CommonJS implementiert - stattdessen implementiert RequireJS AMD was etwas Ähnliches, aber nicht dasselbe ist (auch bekannt als inkompatibel).
Letzteres ist nur ein wichtiger Punkt, den Sie auf Ihrem Weg zum Verständnis von require erkennen müssen.

Um die Frage "Was wird benötigt?" zu beantworten, müssen wir also "nur" wissen, was diese Funktion tut.
Dies lässt sich vielleicht am besten mit einem Code erklären.

Hier ist ein einfache Umsetzung durch Michele Nasti finden Sie den Code auf seiner Github-Seite .

Nennen wir unsere minimalistische Implementierung der require-Funktion "myRequire":

function myRequire(name) 
{
    console.log(`Evaluating file ${name}`);
    if (!(name in myRequire.cache)) {
        console.log(`${name} is not in cache; reading from disk`);
        let code = fs.readFileSync(name, 'utf8');
        let module = { exports: {} };
        myRequire.cache[name] = module;
        let wrapper = Function("require, exports, module", code);
        wrapper(myRequire, module.exports, module);
    }
    console.log(`${name} is in cache. Returning it...`);
    return myRequire.cache[name].exports;
}
myRequire.cache = Object.create(null);
window.require = myRequire;
const stuff = window.require('./main.js');
console.log(stuff);

Wie Sie sehen, wird hier das Objekt "fs" verwendet.
Der Einfachheit halber hat Michele nur das NodeJS-Modul fs importiert:

const fs = require('fs');

Das wäre nicht nötig.
Im Browser könnten Sie also eine einfache Implementierung von require mit einem SYNCHRONOUS XmlHttpRequest vornehmen:

const fs = {
    file: `
    // module.exports = \"Hello World\";

    module.exports = function(){ return 5*3;};

    `
    , getFile(fileName: string, encoding: string): string
    {
        // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests
        let client = new XMLHttpRequest();
        // client.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");

        // open(method, url, async)
        client.open("GET", fileName, false);
        client.send();
        if (client.status === 200)
            return client.responseText;

        return null;
    }

    , readFileSync: function (fileName: string, encoding: string): string
    {
        // this.getFile(fileName, encoding);
        return this.file; // Example, getFile would fetch this file 
    }
};

Im Grunde lädt require also eine JavaScript-Datei herunter, evaluliert sie in einem anonymen Namespace (aka Function) mit den Parametern "require", "exports" und "module" und gibt die Exporte zurück, also die öffentlichen Funktionen und Eigenschaften eines Objekts.

Beachten Sie, dass diese Auswertung rekursiv ist: Sie benötigen Dateien, die ihrerseits wiederum Dateien benötigen können.

Auf diese Weise sind alle "globalen" Variablen, die in Ihrem Modul verwendet werden, Variablen im require-wrapper-function-Namensraum und verschmutzen den globalen Bereich nicht mit unerwünschten Variablen.

Außerdem können Sie auf diese Weise Code wiederverwenden, ohne auf Namespaces angewiesen zu sein, so dass Sie "Modularität" in JavaScript erhalten. "Modularität" in Anführungszeichen, denn das stimmt nicht ganz, denn man kann immer noch window.bla/global.bla schreiben und damit den globalen Bereich verschmutzen... Außerdem wird dadurch eine Trennung zwischen privaten und öffentlichen Aufgaben vorgenommen, wobei die öffentlichen Aufgaben die Ausfuhren sind.

Anstatt nun zu sagen

module.exports = function(){ return 5*3;};

Sie können auch sagen:

function privateSomething()
{
    return 42:
}

function privateSomething2()
{
    return 21:
}

module.exports = {
      getRandomNumber: privateSomething
     ,getHalfRandomNumber: privateSomething2
};

und geben ein Objekt zurück.

Da Ihre Module in einer Funktion mit Parametern ausgewertet werden "require", "exports" und "module" ausgewertet werden, können Ihre Module die nicht deklarierten Variablen "require", "exports" und "module" verwenden, was auf den ersten Blick verwundern mag. Der require-Parameter ist natürlich ein Zeiger auf die require-Funktion, der in einer Variablen gespeichert ist.
Cool, nicht wahr?
So gesehen verliert der Bedarf seinen Zauber und wird einfach.

Die eigentliche require-Funktion führt natürlich noch ein paar weitere Prüfungen durch, aber das ist die Essenz dessen, worauf das hinausläuft.

Außerdem sollten Sie im Jahr 2020 die ECMA-Implementierungen anstelle von require verwenden:

import defaultExport from "module-name";
import * as name from "module-name";
import { export1 } from "module-name";
import { export1 as alias1 } from "module-name";
import { export1 , export2 } from "module-name";
import { foo , bar } from "module-name/path/to/specific/un-exported/file";
import { export1 , export2 as alias2 , [...] } from "module-name";
import defaultExport, { export1 [ , [...] ] } from "module-name";
import defaultExport, * as name from "module-name";
import "module-name";

Und wenn Sie einen dynamischen, nicht statischen Import benötigen (z. B. um ein Polyfill auf der Grundlage des Browsertyps zu laden), gibt es die ECMA-Import-Funktion bzw. das ECMA-Schlüsselwort:

var promise = import("module-name");

Beachten Sie, dass import nicht synchron ist wie require.
Stattdessen ist der Import ein Versprechen, also

var something = require("something");

wird

var something = await import("something");

weil import ein Versprechen zurückgibt (asynchron).

Im Gegensatz zu require ersetzt import also fs.readFileSync durch fs.readFileAsync.

async readFileAsync(fileName, encoding) 
{
    const textDecoder = new TextDecoder(encoding);
    // textDecoder.ignoreBOM = true;
    const response = await fetch(fileName);
    console.log(response.ok);
    console.log(response.status);
    console.log(response.statusText);
    // let json = await response.json();
    // let txt = await response.text();
    // let blo:Blob = response.blob();
    // let ab:ArrayBuffer = await response.arrayBuffer();
    // let fd = await response.formData()
    // Read file almost by line
    // https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader/read#Example_2_-_handling_text_line_by_line
    let buffer = await response.arrayBuffer();
    let file = textDecoder.decode(buffer);
    return file;
} // End Function readFileAsync

Dies erfordert natürlich, dass die Importfunktion ebenfalls asynchron ist .

"use strict";
async function myRequireAsync(name) {
    console.log(`Evaluating file ${name}`);
    if (!(name in myRequireAsync.cache)) {
        console.log(`${name} is not in cache; reading from disk`);
        let code = await fs.readFileAsync(name, 'utf8');
        let module = { exports: {} };
        myRequireAsync.cache[name] = module;
        let wrapper = Function("asyncRequire, exports, module", code);
        await wrapper(myRequireAsync, module.exports, module);
    }
    console.log(`${name} is in cache. Returning it...`);
    return myRequireAsync.cache[name].exports;
}
myRequireAsync.cache = Object.create(null);
window.asyncRequire = myRequireAsync;
async () => {
    const asyncStuff = await window.asyncRequire('./main.js');
    console.log(asyncStuff);
};

Noch besser, oder?
Nun ja, außer, dass es keine ECMA-Möglichkeit gibt, dynamisch synchron zu importieren (ohne Versprechen).

Um die Auswirkungen zu verstehen, sollten Sie unbedingt Lesen Sie hier mehr über promises/async-await falls Sie nicht wissen, was das ist.

Aber ganz einfach gesagt, wenn eine Funktion ein Versprechen zurückgibt, kann sie "abgewartet" werden:

"use strict";
function sleep(interval) 
{
    return new Promise(
        function (resolve, reject) 
        {
            let wait = setTimeout(function () {
            clearTimeout(wait);
            //reject(new Error(`Promise timed out ! (timeout = ${timeout})`));
            resolve();
        }, interval);
    });
}

Das Versprechen würde dann normalerweise wie folgt verwendet werden:

function testSleep() 
{
    sleep(3000).then(function () 
    {
        console.log("Waited for 3 seconds");
    });
}

Aber wenn Sie ein Versprechen zurückgeben, können Sie auch await verwenden, was bedeutet, dass wir den Callback loswerden (sozusagen - tatsächlich wird er im Compiler/Interpreter durch eine State-Machine ersetzt).
Auf diese Weise fühlt sich asynchroner Code wie synchroner Code an, so dass wir jetzt try-catch für die Fehlerbehandlung verwenden können.
Beachten Sie, dass, wenn Sie await in einer Funktion verwenden wollen, diese Funktion als async deklariert werden muss (daher async-await).

async function testSleep() 
{
    await sleep(5000);
    console.log("i waited 5 seconds");
}

Und beachten Sie bitte auch, dass es in JavaScript keine Möglichkeit gibt, eine asynchrone Funktion (blockierend) von einer synchronen Funktion (die Sie kennen) aufzurufen. Wenn Sie also await (auch bekannt als ECMA-Import) verwenden möchten, muss Ihr gesamter Code asynchron sein, was höchstwahrscheinlich ein Problem darstellt, wenn nicht bereits alles asynchron ist...

Ein Beispiel dafür, wo diese vereinfachte Implementierung von require versagt, ist, wenn Sie eine Datei benötigen, die kein gültiges JavaScript ist, z.B. wenn Sie css, html, txt, svg und Bilder oder andere binäre Dateien benötigen.
Und es ist leicht zu erkennen, warum:
Wenn Sie z.B. HTML in einen JavaScript-Funktionskörper einfügen, erhalten Sie natürlich zu Recht

SyntaxError: Unexpected token '<'

wegen Function("bla", "<doctype...")

Wenn Sie dies nun erweitern wollten, um beispielsweise Nicht-Module einzuschließen, könnten Sie einfach die heruntergeladenen Datei-Inhalte auf code.indexOf("module.exports") == -1 und dann z.B. eval("jquery content") anstelle von Func (was gut funktioniert, solange man im Browser ist). Da Downloads mit Fetch/XmlHttpRequests der Same-Origin-Policy unterliegen und die Integrität durch SSL/TLS sichergestellt wird, ist die Verwendung von eval hier eher harmlos, vorausgesetzt, Sie haben die JS-Dateien überprüft, bevor Sie sie Ihrer Website hinzugefügt haben, aber das sollte eigentlich zum Standardbetrieb gehören.

Beachten Sie, dass es mehrere Implementierungen der require-ähnlichen Funktionalität gibt:

  • das CommonJS-Format (CJS) , verwendet in Node.js verwendet eine require-Funktion und module.exports, um Abhängigkeiten und Module zu definieren. Das npm-Ökosystem ist auf diesem Format aufgebaut. (dies wurde oben bereits umgesetzt)

  • das Format Asynchronous Module Definition (AMD) die in Browsern verwendet wird, verwendet eine define-Funktion, um Module zu definieren. (Im Grunde ist dies überkomplizierter archaischer Scheiß die Sie niemals verwenden möchten). Außerdem ist AMD das Format, das von RequireJS (Beachten Sie, dass AMD trotz des Namens, der die Zeichen "require" enthält, absolut NICHT CommonJS ist).

  • das ES-Modul-Format (ESM) . Seit ES6 (ES2015) unterstützt JavaScript ein natives Modulformat. Es verwendet ein export-Schlüsselwort, um die öffentliche API eines Moduls zu exportieren, und ein import-Schlüsselwort, um sie zu importieren. Dies ist das Schlüsselwort, das Sie verwenden sollten wenn Ihnen archaische Browser wie Safari und IE/EdgeHTML völlig egal sind .

  • das System.register-Format entwickelt, um ES6-Module innerhalb von ES5 zu unterstützen. ( derjenige, den Sie verwenden sollten, wenn Sie Unterstützung für ältere Browser benötigen (Safari & IE & alte Versionen von Chrome auf Mobiltelefonen/Tablets), weil es alle Formate laden kann [für einige werden Plugins benötigt], mit zyklischen Abhängigkeiten umgehen kann und CSS und HTML - Definieren Sie Ihre Module jedoch nicht als system.register - das Format ist ziemlich kompliziert, und denken Sie daran, dass es auch die anderen einfacheren Formate lesen kann )

  • das Format Universal Module Definition (UMD) Das Programm ist kompatibel zu allen oben genannten Formaten (außer ECMA) und wird sowohl im Browser als auch in Node.js verwendet. Es ist besonders nützlich wenn Sie Module schreiben, die sowohl in NodeJS als auch im Browser verwendet werden können . Es ist etwas mangelhaft, da es die neuesten ECMA-Module nicht unterstützt (vielleicht wird dies behoben) - verwenden Sie stattdessen System.register.

Wichtige Nebenbemerkung zum Funktionsargument "Exporte":
JavaScript verwendet Call-by-Value-Sharing - was bedeutet, dass Objekte als Zeiger übergeben werden, aber der Zeiger-Wert selbst wird durch Wert übergeben, nicht durch Verweis. Sie können also Exporte nicht überschreiben, indem Sie ihnen ein neues Objekt zuweisen. Wenn Sie exports überschreiben möchten, müssen Sie stattdessen das neue Objekt module.exports zuweisen - denn module ist der Zeiger, der als Wert übergeben wird, exports in module.exports ist jedoch der Verweis auf den ursprünglichen exports-Zeiger.

Wichtige Nebenbemerkung zu Modul-Scope:
Module werden bewertet ONCE und dann zwischengespeichert zu verlangen.
Das bedeutet, dass alle Ihre Module eine Singleton Umfang.
Wenn Sie einen nicht-singletonalen Geltungsbereich wünschen, müssen Sie etwas tun wie:

var x = require("foo.js").createInstance();

oder einfach

var x = require("foo.js")();

mit dem entsprechenden Code, der von Ihrem Modul zurückgegeben wird.

36voto

Sudhir Bastakoti Punkte 97215

Es wird zum Laden von Modulen verwendet. Lassen Sie uns ein einfaches Beispiel verwenden.

In der Datei circle_object.js :

var Circle = function (radius) {
    this.radius = radius
}
Circle.PI = 3.14

Circle.prototype = {
    area: function () {
        return Circle.PI * this.radius * this.radius;
    }
}

Wir können dies nutzen über require , wie:

node> require('circle_object')
{}
node> Circle
{ [Function] PI: 3.14 }
node> var c = new Circle(3)
{ radius: 3 }
node> c.area()

En require() Methode wird zum Laden und Zwischenspeichern von JavaScript-Modulen verwendet. Wenn Sie also ein lokales, relatives JavaScript-Modul in eine Node.js-Anwendung laden wollen, können Sie einfach die Methode require() Methode.

var yourModule = require( "your_module_name" ); //.js file extension is optional

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X