Понимание модулей Node.js: для нескольких требуется вернуть один и тот же объект?


106

У меня вопрос по документации node.js по кешированию модулей :

Модули кэшируются после первой загрузки. Это означает (среди прочего), что каждый вызов require ('foo') будет возвращать один и тот же объект , если он будет разрешен в один и тот же файл.

Множественные вызовы require ('foo') могут не приводить к многократному выполнению кода модуля. Это важная особенность. С его помощью можно возвращать «частично выполненные» объекты, что позволяет загружать транзитивные зависимости, даже если они вызывают циклы.

Что имеется ввиду may?

Я хочу знать, всегда ли require будет возвращать один и тот же объект. Таким образом , в случае , если я нужен модуль 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
Это предложение в документации могло быть написано лучше. Мне кажется, что may not - это то же самое, что и not allowed , т.е. множественные вызовы require ('foo') не могут привести к многократному выполнению кода модуля .
— Lucio Paiva

@LucioPaiva Создал PR, чтобы исправить это: github.com/nodejs/node/pull/23143
— mikemaccana

Они означали [[не могут] вызывать], в отличие от [[могут] не вызывать], в том же смысле, что и «Нет, Тимми, у тебя не должно быть больше шоколада». Так что в этом контексте, я согласен, это написано двусмысленно.
— Ciabaros

Ответы:


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), это не гарантия, что require ('foo ') всегда будет возвращать один и тот же объект, если он разрешается в разные файлы ". Таким образом, вы не можете использовать требуемый кеш для реализации одноэлементного поведения, файловых систем без учета регистра или символических ссылок, что может привести к многократной загрузке одного и того же модуля. Если вам нужны глобальные данные, вам нужно использовать «глобальный» объект.
— Танин,

6

В node.js реализовано какое-то кеширование, которое блокирует чтение файлов node 1000 раз при выполнении некоторых огромных серверных проектов.

Этот кеш указан в require.cacheобъекте. Я должен отметить, что этот объект доступен для чтения / записи, что дает возможность удалять файлы из кеша без остановки процесса.

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

Оу, забыл ответить на вопрос. Изменение экспортируемого объекта не влияет на следующую загрузку модуля. Это вызовет много проблем ... Требовать всегда возвращать новый экземпляр объекта, без ссылки. Редактирование файла и удаление кеша меняют экспортируемый объект.

После выполнения некоторых тестов node.js кэширует module.exports. Модификация require.cache[{module}].exportsзаканчивается новым измененным возвращаемым объектом.


1
Код, который я опубликовал, действительно работает. b.bопределяется значением 2. Итак, в этом примере это тот же объект.
— Xomby

Эта функция, на мой взгляд, весьма полезна. Вопрос в том, могу ли я на это рассчитывать. В документации сказано, mayчто делает это неясным.
— Xomby

Выполнение функции по запросу в a.js ничего не меняет ... Он продолжает кешировать возвращаемый объект. Странно, как работают мои приложения? Единственный способ - это, как описано в документации, выполнение переданной функции после require.
— moe

1
иногда объекты одни и те же. Не полагайтесь на это, передайте один экземпляр модуля, если вам нужно.
— Ricardo Tomasi

есть ли способ вернуть новый объект по требованию (не кешированный объект)?
— Мэтт

3

Поскольку вопрос был опубликован, документ был обновлен, чтобы прояснить, почему изначально использовалось слово «может». Теперь он отвечает на сам вопрос, делая вещи явными (я подчеркиваю, что изменилось):

Модули кэшируются после первой загрузки. Это означает (среди прочего), что каждый вызов require ('foo') будет возвращать один и тот же объект, если он будет разрешен в один и тот же файл.

При условии, что require.cache не изменен, несколько вызовов require ('foo') не приведут к многократному выполнению кода модуля. Это важная особенность. С его помощью можно возвращать «частично выполненные» объекты, что позволяет загружать транзитивные зависимости, даже если они вызывают циклы.


2

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

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

В чем я не уверен, так это в том, что бывают ли случаи недействительности кеша, не зависящие от программиста или ведома, что может сделать возможным случайную перезагрузку одного и того же файла пакета несколько раз.


1

В случае, если причина, по которой вы хотите require(x)каждый раз возвращать новый объект, заключается только в том, что вы напрямую изменяете этот объект - что является случаем, с которым я столкнулся - просто клонируйте его и изменяйте и используйте только клон, например:

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

0

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

drex наблюдает за модулем на предмет обновлений и повторно требует модуль после обновления. Новый код require () d, как если бы новый код - это совершенно другой модуль, поэтому require.cache не проблема.


2
Вопрос был в другом, но drex выглядит действительно круто для разработки. Спасибо!
— Alex Ivasyuv

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.