Розуміння модулів Node.js: кілька вимагає повернення одного і того ж об'єкта?


106

У мене є питання, пов’язане з документацією на node.js щодо кешування модулів :

Модулі кешуються після першого завантаження. Це означає (серед іншого), що кожен виклик, який вимагає ('foo'), поверне точно той самий об’єкт , якби він вирішив би той самий файл.

Декілька викликів, які вимагають ("foo"), можуть не спричинити код модуля кілька разів. Це важлива особливість. З його допомогою "частково зроблені" об'єкти можна повернути, таким чином дозволяючи завантажувати перехідні залежності навіть тоді, коли вони спричинять цикли.

Що означає may?

Я хочу знати, якщо вимога завжди поверне один і той же об’єкт. Таким чином , в разі , якщо я потрібен модуль A в app.jsі змінити об'єкт експорту всередині app.js(той , який вимагає повернення) і після цього потрібно модуль B в app.jsтому , що саме по собі вимагає модуль А , Я завжди отримати модифіковану версію цього об'єкта, або новий один?

// 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
Це речення в документах можна було б краще написати. Мені здається, що може бути не те саме, що не дозволено , тобто декілька викликів, які вимагають ('foo'), не можуть викликати кілька разів модульний код .
— Lucio Paiva

@LucioPaiva Створив PR, щоб виправити це: github.com/nodejs/node/pull/23143
— mikemaccana

Вони означали [[не може] спричинити], на відміну від [[може] не спричинити], в тому ж сенсі, як "Ні, Тіммі, ти можеш не мати більше шоколаду". Тож у цьому контексті, я згоден, це написано неоднозначно.
— Чіабарос

Відповіді:


76

Якщо обидва app.jsі b.jsпроживають в одному проекті (і в тому ж каталозі) , то обидва вони отримають той же екземпляр в A. З документації на node.js :

... кожен виклик до require('foo')повернеться точно такого ж об'єкта , якщо він вирішить отримати один і той же файл .


Інша справа , коли a.js, b.jsі app.jsв різних модулях NPM . Наприклад:

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

В цьому випадку require('a')в app.jsдозволило б на іншу копію , a.jsніж require('a')в b.jsі , отже , повертає другий примірник з A. Є повідомлення в блозі, що описує цю поведінку більш докладно.


1
"і в тому самому каталозі". Я отримав той самий екземпляр, коли [B] знаходився в підпапці, де жив [App].
— Білл Тарбел

1
@BillTarbell Коли я хотів отримати один і той же екземпляр з різних папок, я отримав дві різні.
— MustSeeMelons

1
це останнє застереження все ще вірно тепер, коли npm встановив A, B і APP у плоскій структурі каталогів? Якщо ні, як можна настроїти модуль, щоб кешувати його результати між кількома іншими модулями?
— Майкл

З документації на node.js: "Модулі кешуються на основі їх вирішеного імені файлу. Оскільки модулі можуть вирішувати інше ім'я файлу залежно від місця розташування викличного модуля (завантаження з папок node_modules), це не є гарантією, яка вимагає ('foo ') завжди буде повертати той самий об'єкт, якщо він вирішить різні файли. " Таким чином, ви не можете використовувати кеш вимагати для реалізації поведінки одиночностей, файлових систем, нечутливих до регістру, або символічних посилань, і таке може спричинити завантаження одного і того ж модуля кілька разів. Якщо вам потрібні глобальні дані, вам потрібно використовувати "глобальний" об'єкт.
— Таннін

6

node.js має якийсь реалізований кешування, який блокує вузол зчитування файлів у 1000 разів під час виконання величезних серверних проектів.

Цей кеш вказаний в require.cacheоб'єкті. Я маю зазначити, що цей об’єкт читається / записується, що дає можливість видаляти файли з кешу, не вбиваючи процес.

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

Ой, забув відповісти на запитання. Змінення експортованого об'єкта не впливає на наступне завантаження модуля. Це спричинило б багато проблем ... Вимагати завжди повертати новий екземпляр об'єкта, без посилання. Редагування файлу та видалення кешу змінює експортований об'єкт

Зробивши кілька тестів, node.js кешує модуль.експорт. Змінення require.cache[{module}].exportsзакінчується в новому, модифікованому поверненому об'єкті.


1
Код, який я розмістив, насправді працює. b.bвизначається зі значенням 2. Тож у цьому прикладі це той самий об’єкт.
— Xomby

Це особливість і в моїх очах досить корисна. Питання в тому, чи можу я від цього залежати. У документації зазначено, mayщо робить це незрозумілим.
— Xomby

Виконання функції on demand в a.js нічого не змінює ... Він продовжує кешувати повернутий об'єкт. Дивно, як працюють мої програми? Єдиний спосіб - це, як описано в документах, виконання переданої функції після вимагання.
— moe

1
іноді об’єкти однакові. Не покладайтеся на це, пройдіть навколо одного екземпляра модуля, якщо вам потрібно.
— Рікардо Томасі

чи є спосіб повернути абсолютно новий об’єкт на вимогу (а не кешований об’єкт)?
— Метт

3

Оскільки питання було розміщено, документ було оновлено, щоб було зрозуміло, чому "травень" спочатку використовувався. Тепер він відповідає на саме запитання, роблячи явні речі (мій акцент, щоб показати, що змінилося):

Модулі кешуються після першого завантаження. Це означає (серед іншого), що кожен виклик, який вимагає ('foo'), поверне точно той самий об’єкт, якби він вирішив би той самий файл.

При умови , require.cache не змінюється, кілька викликів вимагати ( «Foo») буде не викликати код модуля буде виконуватися кілька разів. Це важлива особливість. З його допомогою "частково зроблені" об'єкти можна повернути, таким чином дозволяючи завантажувати перехідні залежності навіть тоді, коли вони спричинять цикли.


2

Що я бачив, якщо ім'я модуля вирішиться до файлу, який попередньо завантажений, кешований модуль повернеться, інакше новий файл буде завантажений окремо.

Тобто кешування засноване на фактичному імені файлу, яке стає вирішеним. Це тому, що, як правило, можуть бути різні версії одного пакета, які встановлені на різних рівнях ієрархії файлів і які повинні бути завантажені відповідно.

Що я не впевнений у тому, чим більше є випадки виходу з кешу, які не знаходяться під контролем програміста чи обізнаності, які можуть зробити можливим випадкове перезавантаження того самого файлу пакету кілька разів.


1

У випадку, якщо причина, коли ви хочете require(x)кожен раз повертати свіжий об'єкт, полягає лише в тому, що ви безпосередньо змінюєте цей об’єкт - у тому випадку, в який я зіткнувся - просто клонуйте його та змініть та використовуйте лише клон, як це:

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

0

спробуйте drex : https://github.com/yuryb/drex

drex переглядає модуль для оновлень і чітко повторно вимагає модуль після оновлення. Новий код вимагається () d, як ніби новий код - це абсолютно інший модуль, тому Requ.cache не є проблемою.


2
Питання було іншим, але drex виглядає дуже здорово для розробок. Дякую!
— Олексій Івасюв

0

Коли вам потрібен об’єкт, ви вимагаєте його довідкової адреси, і, вимагаючи об'єкт двічі, ви отримаєте ту саму адресу! Щоб мати копії одного і того ж об’єкта, слід скопіювати (клонувати) його.

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

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

Клонування проводиться декількома способами, ви можете побачити це для отримання додаткової інформації.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.