Zrozumienie modułów Node.js: wiele wymaga zwrócenia tego samego obiektu?


106

Mam pytanie związane z dokumentacją node.js dotyczącą buforowania modułów :

Moduły są buforowane po pierwszym załadowaniu. Oznacza to (między innymi), że każde wywołanie require ('foo') zwróci dokładnie ten sam obiekt , jeśli miałoby dojść do tego samego pliku.

Wielokrotne wywołania require („foo”) mogą nie powodować wielokrotnego wykonywania kodu modułu. To ważna cecha. Dzięki niemu można zwrócić obiekty „częściowo wykonane”, co pozwala na ładowanie zależności przechodnich, nawet jeśli powodowałyby cykle.

Co to znaczy may?

Chcę wiedzieć, czy require zawsze zwróci ten sam obiekt. Tak więc w przypadku I wymagają modułu A w app.jsi zmienić przedmiot wywozu w app.js(tej, która wymaga zwrotu), a potem wymaga modułu B w app.jsktóry sam wymaga modułu A , będę zawsze uzyskać zmodyfikowaną wersję tego obiektu lub nowy jeden?

// 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
To zdanie w dokumentach mogłoby być lepiej napisane. Wydaje mi się, że to nie może być tym samym, co niedozwolone , tj. Wielokrotne wywołania żądania („foo”) nie mogą spowodować wielokrotnego wykonania kodu modułu .
— Lucio Paiva

@LucioPaiva Utworzono PR, aby to naprawić: github.com/nodejs/node/pull/23143
— mikemaccana

Oznaczały [[nie] powodować], w przeciwieństwie do [[może] nie powodować], w tym samym sensie, co „Nie, Timmy, możesz nie mieć więcej czekolady”. Zgadzam się więc w tym kontekście, że jest to napisane niejednoznacznie.
— Ciabaros

Odpowiedzi:


76

Jeśli oba app.jsi b.jszamieszkują w tym samym projekcie (w tym samym katalogu), a następnie obaj otrzymają tę samą instancję z A. Z dokumentacji node.js :

... każde wywołanie funkcji require('foo')zwróci dokładnie ten sam obiekt , jeśli miałoby dojść do tego samego pliku .


Inaczej przedstawia się sytuacja, kiedy a.js, b.jsi app.jssą w różnych modułów NPM . Na przykład:

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

W takim przypadku require('a')w app.jsrozwiąże do innej kopii a.jsniż require('a')w b.jsa zatem zwrócić inną instancję z A. Na blogu znajduje się bardziej szczegółowy opis tego zachowania.


1
„i w tym samym katalogu”. Otrzymałem ten sam przypadek, gdy [B] znajdował się w podfolderze, w którym mieszkała [App].
— Bill Tarbell

1
@BillTarbell Gdy chciałem pobrać tę samą instancję z różnych folderów, otrzymałem dwa różne.
— MustSeeMelons

1
czy to ostatnie zastrzeżenie jest nadal aktualne, ponieważ npm zainstalowałby A, B i APP w płaskiej strukturze katalogów? Jeśli nie, w jaki sposób można skonfigurować moduł, aby buforował wyniki między wieloma innymi modułami?
— Michael

Z dokumentacji node.js: „Moduły są buforowane na podstawie ich rozstrzygniętej nazwy pliku. Ponieważ moduły mogą być tłumaczone na inną nazwę pliku w oparciu o lokalizację modułu wywołującego (ładowanie z folderów node_modules), nie ma gwarancji, że wymagają ('foo ') zawsze zwróci dokładnie ten sam obiekt, jeśli miałoby to oznaczać różne pliki. " Nie można więc używać pamięci podręcznej wymagającej do implementacji zachowania pojedynczego, systemów plików niewrażliwych na wielkość liter lub dowiązań symbolicznych, co może spowodować wielokrotne ładowanie tego samego modułu. Jeśli potrzebujesz danych globalnych, musisz użyć obiektu „global”.
— Tanina

6

W node.js zaimplementowano pewnego rodzaju buforowanie, które blokuje węzeł przed czytaniem plików tysiące razy podczas wykonywania dużych projektów serwerowych.

Ta pamięć podręczna jest wymieniona w require.cacheobiekcie. Muszę zauważyć, że ten obiekt jest do odczytu / zapisu, co daje możliwość usuwania plików z pamięci podręcznej bez przerywania procesu.

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

Ouh, zapomniałem odpowiedzieć na pytanie. Modyfikacja eksportowanego obiektu nie wpływa na następne ładowanie modułu. Spowodowałoby to wiele problemów ... Wymagaj zawsze zwracania nowej instancji obiektu, bez odniesienia. Edycja pliku i usunięcie pamięci podręcznej powoduje zmianę eksportowanego obiektu

Po wykonaniu kilku testów node.js buforuje plik module.exports. Modyfikacja require.cache[{module}].exportskończy się nowym, zmodyfikowanym zwróconym obiektem.


1
Kod, który opublikowałem, faktycznie działa. b.bjest zdefiniowana wartością 2. Więc w tym przykładzie jest to ten sam obiekt.
— Xomby,

To funkcja i moim zdaniem całkiem przydatna. Pytanie brzmi, czy mogę na tym polegać. Dokumentacja mówi, mayco czyni to niejasnym.
— Xomby,

Wykonywanie funkcji na require w a.js niczego nie zmienia ... Utrzymuje buforowanie zwróconego obiektu. Dziwne, jak działają moje aplikacje? Jedynym sposobem jest, zgodnie z opisem w dokumentacji, wykonanie przekazanej funkcji po wymaganiu.
— moe

1
czasami przedmioty są takie same. Nie polegaj na tym, jeśli zajdzie taka potrzeba, pomiń pojedynczą instancję modułu.
— Ricardo Tomasi

czy istnieje sposób na odzyskanie zupełnie nowego obiektu na żądanie (nie obiekt w pamięci podręcznej)?
— Matt

3

Odkąd opublikowano pytanie, dokument został zaktualizowany, aby wyjaśnić, dlaczego pierwotnie użyto słowa „może”. Teraz odpowiada na to pytanie, wyjaśniając rzeczy (podkreślam, co się zmieniło):

Moduły są buforowane po pierwszym załadowaniu. Oznacza to (między innymi), że każde wywołanie require ('foo') zwróci dokładnie ten sam obiekt, jeśli miałoby dojść do tego samego pliku.

Pod warunkiem require.cache nie jest modyfikowany, wiele połączeń do require ( „foo”) będzie nie powodują kod Moduł być wykonywane wielokrotnie. To ważna cecha. Dzięki niemu można zwrócić obiekty „częściowo wykonane”, co pozwala na ładowanie zależności przechodnich, nawet jeśli powodowałyby cykle.


2

Z tego, co widziałem, jeśli nazwa modułu zostanie rozwiązana jako plik załadowany wcześniej, moduł buforowany zostanie zwrócony, w przeciwnym razie nowy plik zostanie załadowany osobno.

Oznacza to, że buforowanie jest oparte na rzeczywistej nazwie pliku, która zostanie rozwiązana. Dzieje się tak, ponieważ generalnie mogą istnieć różne wersje tego samego pakietu, które są instalowane na różnych poziomach hierarchii plików i muszą być odpowiednio ładowane.

Nie jestem pewien, czy istnieją przypadki unieważnienia pamięci podręcznej, które nie są pod kontrolą lub świadomością programisty, co może pozwolić na przypadkowe ponowne załadowanie tego samego pliku pakietu wiele razy.


1

W przypadku, gdy powodem, dla którego chcesz require(x)zwrócić nowy obiekt za każdym razem, jest to, że modyfikujesz ten obiekt bezpośrednio - co jest przypadkiem, na który się natknąłem - po prostu sklonuj go, zmodyfikuj i użyj tylko klonu, na przykład:

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

0

wypróbuj Drex : https://github.com/yuryb/drex

drex obserwuje moduł pod kątem aktualizacji i czysto ponownie wymaga modułu po aktualizacji. Nowy kod jest require () d tak, jakby nowy kod był zupełnie innym modułem, więc require.cache nie stanowi problemu.


2
Pytanie było inne, ale Drex wygląda naprawdę fajnie, jeśli chodzi o programowanie. Dzięki!
— Alex Ivasyuv

0

Kiedy potrzebujesz obiektu, potrzebujesz jego adresu referencyjnego, a wymagając obiektu dwukrotnie, otrzymasz ten sam adres! Aby mieć kopie tego samego obiektu, należy go skopiować (sklonować).

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

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

Klonowanie odbywa się na wiele sposobów, można zobaczyć to , aby uzyskać dodatkowe informacje.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.