Grundlegendes zu Node.js-Modulen: Mehrere erfordern die Rückgabe des gleichen Objekts?


105

Ich habe eine Frage zur Dokumentation von node.j zum Modul-Caching :

Module werden nach dem ersten Laden zwischengespeichert. Dies bedeutet (unter anderem), dass bei jedem Aufruf ('foo') genau dasselbe Objekt zurückgegeben wird, wenn es in dieselbe Datei aufgelöst wird.

Bei mehreren erforderlichen Anrufen ('foo') wird der Modulcode möglicherweise nicht mehrmals ausgeführt. Dies ist ein wichtiges Merkmal. Damit können "teilweise erledigte" Objekte zurückgegeben werden, so dass transitive Abhängigkeiten geladen werden können, selbst wenn sie Zyklen verursachen würden.

Womit ist gemeint may?

Ich möchte wissen, ob bei Bedarf immer das gleiche Objekt zurückgegeben wird. Also, falls ich ein Modul benötigen A in app.jsund die Exporte Objekt ändern innerhalb app.js(die, die Rückkehr erfordert) und danach benötigen ein Modul B in app.jsdas selbst Modul benötigt A , werde ich immer bekommen die modifizierte Version des Objekts, oder einen neuen einer?

// app.js

var a = require('./a');
a.b = 2;
console.log(a.b); //2

var b = require('./b');
console.log(b.b); //2

// a.js

exports.a = 1;

// b.js

module.exports = require('./a');

7
Dieser Satz in den Dokumenten hätte besser geschrieben werden können. Es scheint mir, dass dies möglicherweise nicht dasselbe ist wie nicht erlaubt , dh dass mehrere Aufrufe ('foo') nicht dazu führen können, dass der Modulcode mehrmals ausgeführt wird .
Lucio Paiva

@ LucioPaiva Erstellt eine PR, um es zu beheben: github.com/nodejs/node/pull/23143
mikemaccana

Sie bedeuteten im Gegensatz zu "Nein, Timmy, du darfst keine Schokolade mehr haben" im Gegensatz zu "möglicherweise nicht verursachen". In diesem Zusammenhang stimme ich zu, dass dies mehrdeutig geschrieben ist.
Ciabaros

Antworten:


76

Wenn beide app.jsund b.jsresidieren im selben Projekt (und im selben Verzeichnis) , dann beide erhalten die gleiche Instanz von A. Aus der Dokumentation zu node.js :

... bei jedem Aufruf von require('foo')wird genau das gleiche Objekt zurückgegeben, wenn es in dieselbe Datei aufgelöst wird .


Anders ist die Situation , wenn a.js, b.jsund app.jssind in verschiedenen npm Module . Beispielsweise:

[APP] --> [A], [B]
[B]   --> [A]

In diesem Fall würde das require('a')in app.jsin eine andere Kopie von a.jsals require('a')in aufgelöst b.jsund daher eine andere Instanz von zurückgegeben A. Es gibt einen Blog-Beitrag , der dieses Verhalten ausführlicher beschreibt.


1
"und im selben Verzeichnis". Ich habe dieselbe Instanz erhalten, als [B] sich in einem Unterordner befand, in dem [App] lebte.
Bill Tarbell

1
@ BillTarbell Als ich dieselbe Instanz aus verschiedenen Ordnern abrufen wollte, bekam ich zwei verschiedene.
MustSeeMelons

1
Ist diese letzte Einschränkung jetzt noch wahr, da npm A, B und APP in einer flachen Verzeichnisstruktur installieren würde? Wenn nicht, wie kann ein Modul so eingerichtet werden, dass seine Ergebnisse zwischen mehreren anderen Modulen zwischengespeichert werden?
Michael

In der Dokumentation zu node.js heißt es: "Module werden basierend auf ihrem aufgelösten Dateinamen zwischengespeichert. Da Module möglicherweise basierend auf dem Speicherort des aufrufenden Moduls in einen anderen Dateinamen aufgelöst werden (Laden aus den Ordnern node_modules), ist dies keine Garantie, die erforderlich ist ('foo ') gibt immer genau das gleiche Objekt zurück, wenn es in verschiedene Dateien aufgelöst wird. " Sie können also nicht die benötigen Cache verwenden Singleton Verhalten, Groß- und Kleinschreibung Dateisysteme oder symbolische Links und so das gleiche Modul verursacht mehrmals geladen werden kann zu implementieren. Wenn Sie globale Daten benötigen, müssen Sie das "globale" Objekt verwenden.
Tannin

6

In node.js ist eine Art Caching implementiert, das den Knoten daran hindert, Dateien tausende Male zu lesen, während einige große Serverprojekte ausgeführt werden.

Dieser Cache wird im require.cacheObjekt aufgelistet . Ich muss beachten, dass dieses Objekt lesbar / beschreibbar ist, wodurch Dateien aus dem Cache gelöscht werden können, ohne den Prozess abzubrechen.

http://nodejs.org/docs/latest/api/globals.html#require.cache

Oh, ich habe vergessen, die Frage zu beantworten. Das Ändern des exportierten Objekts wirkt sich nicht auf das nächste Laden des Moduls aus. Dies würde viel Ärger verursachen ... Erforderlich, immer eine neue Instanz des Objekts zurückzugeben, keine Referenz. Durch Bearbeiten der Datei und Löschen des Caches wird das exportierte Objekt geändert

Nach einigen Tests speichert node.js die module.exports im Cache. Das Ändern require.cache[{module}].exportsendet in einem neuen, geänderten zurückgegebenen Objekt.


1
Der Code, den ich gepostet habe, funktioniert tatsächlich. b.bwird mit dem Wert definiert 2. In diesem Beispiel ist es also dasselbe Objekt.
Xomby

Das ist eine Funktion und in meinen Augen sehr nützlich. Die Frage ist, ob ich mich darauf verlassen kann. Die Dokumentation sagt may, was es unklar macht.
Xomby

Das Ausführen einer Funktion bei Bedarf in a.js ändert nichts ... Das zurückgegebene Objekt wird weiterhin zwischengespeichert. Seltsam, wie funktionieren meine Apps? Die einzige Möglichkeit besteht darin, wie in den Dokumenten beschrieben, eine übergebene Funktion nach Bedarf auszuführen.
Moe

1
manchmal sind die Objekte gleich. Verlassen Sie sich nicht darauf, sondern geben Sie bei Bedarf eine einzelne Modulinstanz weiter.
Ricardo Tomasi

Gibt es eine Möglichkeit, ein brandneues Objekt für eine Anforderung zurückzugewinnen (nicht das zwischengespeicherte Objekt)?
Matt

3

Seit die Frage veröffentlicht wurde, wurde das Dokument aktualisiert, um zu verdeutlichen, warum "may" ursprünglich verwendet wurde. Es beantwortet nun die Frage selbst, indem es die Dinge explizit macht (meine Betonung darauf, zu zeigen, was sich geändert hat):

Module werden nach dem ersten Laden zwischengespeichert. Dies bedeutet (unter anderem), dass bei jedem erforderlichen Aufruf ('foo') genau dasselbe Objekt zurückgegeben wird, wenn es in dieselbe Datei aufgelöst wird.

Vorausgesetzt require.cache wird nicht geändert, um mehr Anrufe erfordern ( ‚foo‘) wird das Modul Code nicht dazu führen , dass mehrere Male ausgeführt werden. Dies ist ein wichtiges Merkmal. Damit können "teilweise erledigte" Objekte zurückgegeben werden, so dass transitive Abhängigkeiten geladen werden können, selbst wenn sie Zyklen verursachen würden.


2

Wenn sich der Modulname in eine zuvor geladene Datei auflöst, wird das zwischengespeicherte Modul zurückgegeben, andernfalls wird die neue Datei separat geladen.

Das heißt, das Caching basiert auf dem tatsächlichen Dateinamen , der aufgelöst wird. Dies liegt daran, dass im Allgemeinen verschiedene Versionen desselben Pakets auf verschiedenen Ebenen der Dateihierarchie installiert sein können und entsprechend geladen werden müssen.

Ich bin mir nicht sicher, ob es Fälle von Cache-Ungültigkeit gibt, die nicht unter der Kontrolle oder Kenntnis des Programmierers stehen und die es möglicherweise ermöglichen, dieselbe Paketdatei versehentlich mehrmals neu zu laden.


1

Falls der Grund, warum Sie require(x)jedes Mal ein neues Objekt zurückgeben möchten , nur darin besteht, dass Sie dieses Objekt direkt ändern - was ein Fall ist, auf den ich gestoßen bin -, klonen Sie es einfach und ändern und verwenden Sie nur den Klon wie folgt:

var a = require('./a');
a = JSON.parse(JSON.stringify(a));

0

Versuchen Sie es mit drex : https://github.com/yuryb/drex

drex überwacht ein Modul auf Updates und benötigt das Modul nach dem Update erneut. Neuer Code wird benötigt () d, als ob der neue Code ein völlig anderes Modul wäre, also ist require.cache kein Problem.


2
Die Frage war eine andere, aber drex sieht für Entwicklungssachen wirklich cool aus. Vielen Dank!
Alex Ivasyuv

0

Wenn Sie ein Objekt benötigen, benötigen Sie dessen Referenzadresse. Wenn Sie das Objekt zweimal benötigen, erhalten Sie dieselbe Adresse! Um Kopien desselben Objekts zu erhalten, sollten Sie es kopieren (klonen).

var obj = require('./obj');

a = JSON.parse(JSON.stringify(obj));
b = JSON.parse(JSON.stringify(obj));
c = JSON.parse(JSON.stringify(obj));

Klonen ist in vielfältiger Weise getan, können Sie sehen , diese , für weitere Informationen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.